1#[cfg(all(unix, not(target_arch = "wasm32")))]
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::{once, repeat};
11use std::mem;
12use std::path::{Component, Path, PathBuf};
13use std::str;
14
15use crate::other;
16use crate::EntryType;
17
18#[cfg(all(any(unix, windows), not(target_arch = "wasm32")))]
24pub const DETERMINISTIC_TIMESTAMP: u64 = 1153704088;
25
26pub(crate) const BLOCK_SIZE: u64 = 512;
27
28pub(crate) const GNU_SPARSE_HEADERS_COUNT: usize = 4;
29
30pub(crate) const GNU_EXT_SPARSE_HEADERS_COUNT: usize = 21;
31
32#[repr(C)]
34#[allow(missing_docs)]
35pub struct Header {
36 bytes: [u8; BLOCK_SIZE as usize],
37}
38
39#[derive(Clone, Copy, PartialEq, Eq, Debug)]
42#[non_exhaustive]
43pub enum HeaderMode {
44 Complete,
47
48 Deterministic,
51}
52
53#[repr(C)]
55#[allow(missing_docs)]
56pub struct OldHeader {
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 linkflag: [u8; 1],
65 pub linkname: [u8; 100],
66 pub pad: [u8; 255],
67}
68
69#[repr(C)]
71#[allow(missing_docs)]
72pub struct UstarHeader {
73 pub name: [u8; 100],
74 pub mode: [u8; 8],
75 pub uid: [u8; 8],
76 pub gid: [u8; 8],
77 pub size: [u8; 12],
78 pub mtime: [u8; 12],
79 pub cksum: [u8; 8],
80 pub typeflag: [u8; 1],
81 pub linkname: [u8; 100],
82
83 pub magic: [u8; 6],
85 pub version: [u8; 2],
86 pub uname: [u8; 32],
87 pub gname: [u8; 32],
88 pub dev_major: [u8; 8],
89 pub dev_minor: [u8; 8],
90 pub prefix: [u8; 155],
91 pub pad: [u8; 12],
92}
93
94#[repr(C)]
96#[allow(missing_docs)]
97pub struct GnuHeader {
98 pub name: [u8; 100],
99 pub mode: [u8; 8],
100 pub uid: [u8; 8],
101 pub gid: [u8; 8],
102 pub size: [u8; 12],
103 pub mtime: [u8; 12],
104 pub cksum: [u8; 8],
105 pub typeflag: [u8; 1],
106 pub linkname: [u8; 100],
107
108 pub magic: [u8; 6],
110 pub version: [u8; 2],
111 pub uname: [u8; 32],
112 pub gname: [u8; 32],
113 pub dev_major: [u8; 8],
114 pub dev_minor: [u8; 8],
115 pub atime: [u8; 12],
116 pub ctime: [u8; 12],
117 pub offset: [u8; 12],
118 pub longnames: [u8; 4],
119 pub unused: [u8; 1],
120 pub sparse: [GnuSparseHeader; GNU_SPARSE_HEADERS_COUNT],
121 pub isextended: [u8; 1],
122 pub realsize: [u8; 12],
123 pub pad: [u8; 17],
124}
125
126#[repr(C)]
130#[allow(missing_docs)]
131pub struct GnuSparseHeader {
132 pub offset: [u8; 12],
133 pub numbytes: [u8; 12],
134}
135
136#[repr(C)]
141#[allow(missing_docs)]
142pub struct GnuExtSparseHeader {
143 pub sparse: [GnuSparseHeader; GNU_EXT_SPARSE_HEADERS_COUNT],
144 pub isextended: [u8; 1],
145 pub padding: [u8; 7],
146}
147
148impl Header {
149 pub fn new_gnu() -> Header {
155 let mut header = Header {
156 bytes: [0; BLOCK_SIZE as usize],
157 };
158 unsafe {
159 let gnu = cast_mut::<_, GnuHeader>(&mut header);
160 gnu.magic = *b"ustar ";
161 gnu.version = *b" \0";
162 }
163 header.set_mtime(0);
164 header
165 }
166
167 pub fn new_ustar() -> Header {
175 let mut header = Header {
176 bytes: [0; BLOCK_SIZE as usize],
177 };
178 unsafe {
179 let gnu = cast_mut::<_, UstarHeader>(&mut header);
180 gnu.magic = *b"ustar\0";
181 gnu.version = *b"00";
182 }
183 header.set_mtime(0);
184 header
185 }
186
187 pub fn new_old() -> Header {
194 let mut header = Header {
195 bytes: [0; BLOCK_SIZE as usize],
196 };
197 header.set_mtime(0);
198 header
199 }
200
201 fn is_ustar(&self) -> bool {
202 let ustar = unsafe { cast::<_, UstarHeader>(self) };
203 ustar.magic[..] == b"ustar\0"[..] && ustar.version[..] == b"00"[..]
204 }
205
206 fn is_gnu(&self) -> bool {
207 let ustar = unsafe { cast::<_, UstarHeader>(self) };
208 ustar.magic[..] == b"ustar "[..] && ustar.version[..] == b" \0"[..]
209 }
210
211 pub fn as_old(&self) -> &OldHeader {
216 unsafe { cast(self) }
217 }
218
219 pub fn as_old_mut(&mut self) -> &mut OldHeader {
221 unsafe { cast_mut(self) }
222 }
223
224 pub fn as_ustar(&self) -> Option<&UstarHeader> {
234 if self.is_ustar() {
235 Some(unsafe { cast(self) })
236 } else {
237 None
238 }
239 }
240
241 pub fn as_ustar_mut(&mut self) -> Option<&mut UstarHeader> {
243 if self.is_ustar() {
244 Some(unsafe { cast_mut(self) })
245 } else {
246 None
247 }
248 }
249
250 pub fn as_gnu(&self) -> Option<&GnuHeader> {
260 if self.is_gnu() {
261 Some(unsafe { cast(self) })
262 } else {
263 None
264 }
265 }
266
267 pub fn as_gnu_mut(&mut self) -> Option<&mut GnuHeader> {
269 if self.is_gnu() {
270 Some(unsafe { cast_mut(self) })
271 } else {
272 None
273 }
274 }
275
276 pub fn from_byte_slice(bytes: &[u8]) -> &Header {
280 assert_eq!(bytes.len(), mem::size_of::<Header>());
281 assert_eq!(mem::align_of_val(bytes), mem::align_of::<Header>());
282 unsafe { &*(bytes.as_ptr() as *const Header) }
283 }
284
285 pub fn as_bytes(&self) -> &[u8; BLOCK_SIZE as usize] {
287 &self.bytes
288 }
289
290 pub fn as_mut_bytes(&mut self) -> &mut [u8; BLOCK_SIZE as usize] {
292 &mut self.bytes
293 }
294
295 pub fn set_metadata(&mut self, meta: &fs::Metadata) {
302 self.fill_from(meta, HeaderMode::Complete);
303 }
304
305 pub fn set_metadata_in_mode(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
308 self.fill_from(meta, mode);
309 }
310
311 pub fn entry_size(&self) -> io::Result<u64> {
320 num_field_wrapper_from(&self.as_old().size).map_err(|err| {
321 io::Error::new(
322 err.kind(),
323 format!("{} when getting size for {}", err, self.path_lossy()),
324 )
325 })
326 }
327
328 pub fn size(&self) -> io::Result<u64> {
332 if self.entry_type().is_gnu_sparse() {
333 self.as_gnu()
334 .ok_or_else(|| other("sparse header was not a gnu header"))
335 .and_then(|h| h.real_size())
336 } else {
337 self.entry_size()
338 }
339 }
340
341 pub fn set_size(&mut self, size: u64) {
343 num_field_wrapper_into(&mut self.as_old_mut().size, size);
344 }
345
346 pub fn path(&self) -> io::Result<Cow<'_, Path>> {
354 bytes2path(self.path_bytes())
355 }
356
357 pub fn path_bytes(&self) -> Cow<'_, [u8]> {
365 if let Some(ustar) = self.as_ustar() {
366 ustar.path_bytes()
367 } else {
368 let name = truncate(&self.as_old().name);
369 Cow::Borrowed(name)
370 }
371 }
372
373 fn path_lossy(&self) -> String {
375 String::from_utf8_lossy(&self.path_bytes()).to_string()
376 }
377
378 pub fn set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
390 self.set_path_inner(p.as_ref(), false)
391 }
392
393 pub(crate) fn set_truncated_path_for_gnu_header<P: AsRef<Path>>(
397 &mut self,
398 p: P,
399 ) -> io::Result<()> {
400 self.set_path_inner(p.as_ref(), true)
401 }
402
403 fn set_path_inner(&mut self, path: &Path, is_truncated_gnu_long_path: bool) -> io::Result<()> {
404 if let Some(ustar) = self.as_ustar_mut() {
405 return ustar.set_path(path);
406 }
407 if is_truncated_gnu_long_path {
408 copy_path_into_gnu_long(&mut self.as_old_mut().name, path, false)
409 } else {
410 copy_path_into(&mut self.as_old_mut().name, path, false)
411 }
412 .map_err(|err| {
413 io::Error::new(
414 err.kind(),
415 format!("{} when setting path for {}", err, self.path_lossy()),
416 )
417 })
418 }
419
420 pub fn link_name(&self) -> io::Result<Option<Cow<'_, Path>>> {
429 match self.link_name_bytes() {
430 Some(bytes) => bytes2path(bytes).map(Some),
431 None => Ok(None),
432 }
433 }
434
435 pub fn link_name_bytes(&self) -> Option<Cow<'_, [u8]>> {
443 let old = self.as_old();
444 if old.linkname[0] != 0 {
445 Some(Cow::Borrowed(truncate(&old.linkname)))
446 } else {
447 None
448 }
449 }
450
451 pub fn set_link_name<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
460 self._set_link_name(p.as_ref())
461 }
462
463 fn _set_link_name(&mut self, path: &Path) -> io::Result<()> {
464 copy_path_into(&mut self.as_old_mut().linkname, path, true).map_err(|err| {
465 io::Error::new(
466 err.kind(),
467 format!("{} when setting link name for {}", err, self.path_lossy()),
468 )
469 })
470 }
471
472 pub fn set_link_name_literal<P: AsRef<[u8]>>(&mut self, p: P) -> io::Result<()> {
477 self._set_link_name_literal(p.as_ref())
478 }
479
480 fn _set_link_name_literal(&mut self, bytes: &[u8]) -> io::Result<()> {
481 copy_into(&mut self.as_old_mut().linkname, bytes)
482 }
483
484 pub fn mode(&self) -> io::Result<u32> {
488 octal_from(&self.as_old().mode)
489 .map(|u| u as u32)
490 .map_err(|err| {
491 io::Error::new(
492 err.kind(),
493 format!("{} when getting mode for {}", err, self.path_lossy()),
494 )
495 })
496 }
497
498 pub fn set_mode(&mut self, mode: u32) {
500 octal_into(&mut self.as_old_mut().mode, mode);
501 }
502
503 pub fn uid(&self) -> io::Result<u64> {
507 num_field_wrapper_from(&self.as_old().uid).map_err(|err| {
508 io::Error::new(
509 err.kind(),
510 format!("{} when getting uid for {}", err, self.path_lossy()),
511 )
512 })
513 }
514
515 pub fn set_uid(&mut self, uid: u64) {
517 num_field_wrapper_into(&mut self.as_old_mut().uid, uid);
518 }
519
520 pub fn gid(&self) -> io::Result<u64> {
522 num_field_wrapper_from(&self.as_old().gid).map_err(|err| {
523 io::Error::new(
524 err.kind(),
525 format!("{} when getting gid for {}", err, self.path_lossy()),
526 )
527 })
528 }
529
530 pub fn set_gid(&mut self, gid: u64) {
532 num_field_wrapper_into(&mut self.as_old_mut().gid, gid);
533 }
534
535 pub fn mtime(&self) -> io::Result<u64> {
537 num_field_wrapper_from(&self.as_old().mtime).map_err(|err| {
538 io::Error::new(
539 err.kind(),
540 format!("{} when getting mtime for {}", err, self.path_lossy()),
541 )
542 })
543 }
544
545 pub fn set_mtime(&mut self, mtime: u64) {
550 num_field_wrapper_into(&mut self.as_old_mut().mtime, mtime);
551 }
552
553 pub fn username(&self) -> Result<Option<&str>, str::Utf8Error> {
560 match self.username_bytes() {
561 Some(bytes) => str::from_utf8(bytes).map(Some),
562 None => Ok(None),
563 }
564 }
565
566 pub fn username_bytes(&self) -> Option<&[u8]> {
571 if let Some(ustar) = self.as_ustar() {
572 Some(ustar.username_bytes())
573 } else if let Some(gnu) = self.as_gnu() {
574 Some(gnu.username_bytes())
575 } else {
576 None
577 }
578 }
579
580 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
585 if let Some(ustar) = self.as_ustar_mut() {
586 return ustar.set_username(name);
587 }
588 if let Some(gnu) = self.as_gnu_mut() {
589 gnu.set_username(name)
590 } else {
591 Err(other("not a ustar or gnu archive, cannot set username"))
592 }
593 }
594
595 pub fn groupname(&self) -> Result<Option<&str>, str::Utf8Error> {
602 match self.groupname_bytes() {
603 Some(bytes) => str::from_utf8(bytes).map(Some),
604 None => Ok(None),
605 }
606 }
607
608 pub fn groupname_bytes(&self) -> Option<&[u8]> {
613 if let Some(ustar) = self.as_ustar() {
614 Some(ustar.groupname_bytes())
615 } else if let Some(gnu) = self.as_gnu() {
616 Some(gnu.groupname_bytes())
617 } else {
618 None
619 }
620 }
621
622 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
627 if let Some(ustar) = self.as_ustar_mut() {
628 return ustar.set_groupname(name);
629 }
630 if let Some(gnu) = self.as_gnu_mut() {
631 gnu.set_groupname(name)
632 } else {
633 Err(other("not a ustar or gnu archive, cannot set groupname"))
634 }
635 }
636
637 pub fn device_major(&self) -> io::Result<Option<u32>> {
645 if let Some(ustar) = self.as_ustar() {
646 ustar.device_major().map(Some)
647 } else if let Some(gnu) = self.as_gnu() {
648 gnu.device_major().map(Some)
649 } else {
650 Ok(None)
651 }
652 }
653
654 pub fn set_device_major(&mut self, major: u32) -> io::Result<()> {
659 if let Some(ustar) = self.as_ustar_mut() {
660 ustar.set_device_major(major);
661 Ok(())
662 } else if let Some(gnu) = self.as_gnu_mut() {
663 gnu.set_device_major(major);
664 Ok(())
665 } else {
666 Err(other("not a ustar or gnu archive, cannot set dev_major"))
667 }
668 }
669
670 pub fn device_minor(&self) -> io::Result<Option<u32>> {
678 if let Some(ustar) = self.as_ustar() {
679 ustar.device_minor().map(Some)
680 } else if let Some(gnu) = self.as_gnu() {
681 gnu.device_minor().map(Some)
682 } else {
683 Ok(None)
684 }
685 }
686
687 pub fn set_device_minor(&mut self, minor: u32) -> io::Result<()> {
692 if let Some(ustar) = self.as_ustar_mut() {
693 ustar.set_device_minor(minor);
694 Ok(())
695 } else if let Some(gnu) = self.as_gnu_mut() {
696 gnu.set_device_minor(minor);
697 Ok(())
698 } else {
699 Err(other("not a ustar or gnu archive, cannot set dev_minor"))
700 }
701 }
702
703 pub fn entry_type(&self) -> EntryType {
705 EntryType::new(self.as_old().linkflag[0])
706 }
707
708 pub fn set_entry_type(&mut self, ty: EntryType) {
710 self.as_old_mut().linkflag = [ty.as_byte()];
711 }
712
713 pub fn cksum(&self) -> io::Result<u32> {
717 octal_from(&self.as_old().cksum)
718 .map(|u| u as u32)
719 .map_err(|err| {
720 io::Error::new(
721 err.kind(),
722 format!("{} when getting cksum for {}", err, self.path_lossy()),
723 )
724 })
725 }
726
727 pub fn set_cksum(&mut self) {
730 let cksum = self.calculate_cksum();
731 octal_into(&mut self.as_old_mut().cksum, cksum);
732 }
733
734 fn calculate_cksum(&self) -> u32 {
735 let old = self.as_old();
736 let start = old as *const _ as usize;
737 let cksum_start = old.cksum.as_ptr() as *const _ as usize;
738 let offset = cksum_start - start;
739 let len = old.cksum.len();
740 self.bytes[0..offset]
741 .iter()
742 .chain(repeat(&b' ').take(len))
743 .chain(&self.bytes[offset + len..])
744 .fold(0, |a, b| a + (*b as u32))
745 }
746
747 fn fill_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
748 self.fill_platform_from(meta, mode);
749 self.set_size(if meta.is_dir() || meta.file_type().is_symlink() {
751 0
752 } else {
753 meta.len()
754 });
755 if let Some(ustar) = self.as_ustar_mut() {
756 ustar.set_device_major(0);
757 ustar.set_device_minor(0);
758 }
759 if let Some(gnu) = self.as_gnu_mut() {
760 gnu.set_device_major(0);
761 gnu.set_device_minor(0);
762 }
763 }
764
765 #[cfg(target_arch = "wasm32")]
766 #[allow(unused_variables)]
767 fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
768 unimplemented!();
769 }
770
771 #[cfg(all(unix, not(target_arch = "wasm32")))]
772 fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
773 match mode {
774 HeaderMode::Complete => {
775 self.set_mtime(meta.mtime() as u64);
776 self.set_uid(meta.uid() as u64);
777 self.set_gid(meta.gid() as u64);
778 self.set_mode(meta.mode());
779 }
780 HeaderMode::Deterministic => {
781 self.set_mtime(DETERMINISTIC_TIMESTAMP);
786
787 self.set_uid(0);
788 self.set_gid(0);
789
790 let fs_mode = if meta.is_dir() || (0o100 & meta.mode() == 0o100) {
792 0o755
793 } else {
794 0o644
795 };
796 self.set_mode(fs_mode);
797 }
798 }
799
800 self.set_entry_type(entry_type(meta.mode()));
811
812 fn entry_type(mode: u32) -> EntryType {
813 match mode as libc::mode_t & libc::S_IFMT {
814 libc::S_IFREG => EntryType::file(),
815 libc::S_IFLNK => EntryType::symlink(),
816 libc::S_IFCHR => EntryType::character_special(),
817 libc::S_IFBLK => EntryType::block_special(),
818 libc::S_IFDIR => EntryType::dir(),
819 libc::S_IFIFO => EntryType::fifo(),
820 _ => EntryType::new(b' '),
821 }
822 }
823 }
824
825 #[cfg(windows)]
826 fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
827 match mode {
829 HeaderMode::Complete => {
830 self.set_uid(0);
831 self.set_gid(0);
832 let mtime = (meta.last_write_time() / (1_000_000_000 / 100)) - 11644473600;
837 self.set_mtime(mtime);
838 let fs_mode = {
839 const FILE_ATTRIBUTE_READONLY: u32 = 0x00000001;
840 let readonly = meta.file_attributes() & FILE_ATTRIBUTE_READONLY;
841 match (meta.is_dir(), readonly != 0) {
842 (true, false) => 0o755,
843 (true, true) => 0o555,
844 (false, false) => 0o644,
845 (false, true) => 0o444,
846 }
847 };
848 self.set_mode(fs_mode);
849 }
850 HeaderMode::Deterministic => {
851 self.set_uid(0);
852 self.set_gid(0);
853 self.set_mtime(DETERMINISTIC_TIMESTAMP); let fs_mode = if meta.is_dir() { 0o755 } else { 0o644 };
855 self.set_mode(fs_mode);
856 }
857 }
858
859 let ft = meta.file_type();
860 self.set_entry_type(if ft.is_dir() {
861 EntryType::dir()
862 } else if ft.is_file() {
863 EntryType::file()
864 } else if ft.is_symlink() {
865 EntryType::symlink()
866 } else {
867 EntryType::new(b' ')
868 });
869 }
870
871 fn debug_fields(&self, b: &mut fmt::DebugStruct) {
872 if let Ok(entry_size) = self.entry_size() {
873 b.field("entry_size", &entry_size);
874 }
875 if let Ok(size) = self.size() {
876 b.field("size", &size);
877 }
878 if let Ok(path) = self.path() {
879 b.field("path", &path);
880 }
881 if let Ok(link_name) = self.link_name() {
882 b.field("link_name", &link_name);
883 }
884 if let Ok(mode) = self.mode() {
885 b.field("mode", &DebugAsOctal(mode));
886 }
887 if let Ok(uid) = self.uid() {
888 b.field("uid", &uid);
889 }
890 if let Ok(gid) = self.gid() {
891 b.field("gid", &gid);
892 }
893 if let Ok(mtime) = self.mtime() {
894 b.field("mtime", &mtime);
895 }
896 if let Ok(username) = self.username() {
897 b.field("username", &username);
898 }
899 if let Ok(groupname) = self.groupname() {
900 b.field("groupname", &groupname);
901 }
902 if let Ok(device_major) = self.device_major() {
903 b.field("device_major", &device_major);
904 }
905 if let Ok(device_minor) = self.device_minor() {
906 b.field("device_minor", &device_minor);
907 }
908 if let Ok(cksum) = self.cksum() {
909 b.field("cksum", &cksum);
910 b.field("cksum_valid", &(cksum == self.calculate_cksum()));
911 }
912 }
913}
914
915struct DebugAsOctal<T>(T);
916
917impl<T: fmt::Octal> fmt::Debug for DebugAsOctal<T> {
918 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
919 fmt::Octal::fmt(&self.0, f)
920 }
921}
922
923unsafe fn cast<T, U>(a: &T) -> &U {
924 assert_eq!(mem::size_of_val(a), mem::size_of::<U>());
925 assert_eq!(mem::align_of_val(a), mem::align_of::<U>());
926 &*(a as *const T as *const U)
927}
928
929unsafe fn cast_mut<T, U>(a: &mut T) -> &mut U {
930 assert_eq!(mem::size_of_val(a), mem::size_of::<U>());
931 assert_eq!(mem::align_of_val(a), mem::align_of::<U>());
932 &mut *(a as *mut T as *mut U)
933}
934
935impl Clone for Header {
936 fn clone(&self) -> Header {
937 Header { bytes: self.bytes }
938 }
939}
940
941impl fmt::Debug for Header {
942 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
943 if let Some(me) = self.as_ustar() {
944 me.fmt(f)
945 } else if let Some(me) = self.as_gnu() {
946 me.fmt(f)
947 } else {
948 self.as_old().fmt(f)
949 }
950 }
951}
952
953impl OldHeader {
954 pub fn as_header(&self) -> &Header {
956 unsafe { cast(self) }
957 }
958
959 pub fn as_header_mut(&mut self) -> &mut Header {
961 unsafe { cast_mut(self) }
962 }
963}
964
965impl fmt::Debug for OldHeader {
966 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
967 let mut f = f.debug_struct("OldHeader");
968 self.as_header().debug_fields(&mut f);
969 f.finish()
970 }
971}
972
973impl UstarHeader {
974 pub fn path_bytes(&self) -> Cow<'_, [u8]> {
976 if self.prefix[0] == 0 && !self.name.contains(&b'\\') {
977 Cow::Borrowed(truncate(&self.name))
978 } else {
979 let mut bytes = Vec::new();
980 let prefix = truncate(&self.prefix);
981 if !prefix.is_empty() {
982 bytes.extend_from_slice(prefix);
983 bytes.push(b'/');
984 }
985 bytes.extend_from_slice(truncate(&self.name));
986 Cow::Owned(bytes)
987 }
988 }
989
990 fn path_lossy(&self) -> String {
992 String::from_utf8_lossy(&self.path_bytes()).to_string()
993 }
994
995 pub fn set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
997 self._set_path(p.as_ref())
998 }
999
1000 fn _set_path(&mut self, path: &Path) -> io::Result<()> {
1001 let bytes = path2bytes(path)?;
1010 let (maxnamelen, maxprefixlen) = (self.name.len(), self.prefix.len());
1011 if bytes.len() <= maxnamelen {
1012 copy_path_into(&mut self.name, path, false).map_err(|err| {
1013 io::Error::new(
1014 err.kind(),
1015 format!("{} when setting path for {}", err, self.path_lossy()),
1016 )
1017 })?;
1018 } else {
1019 let mut prefix = path;
1020 let mut prefixlen;
1021 loop {
1022 match prefix.parent() {
1023 Some(parent) => prefix = parent,
1024 None => {
1025 return Err(other(&format!(
1026 "path cannot be split to be inserted into archive: {}",
1027 path.display()
1028 )));
1029 }
1030 }
1031 prefixlen = path2bytes(prefix)?.len();
1032 if prefixlen <= maxprefixlen {
1033 break;
1034 }
1035 }
1036 copy_path_into(&mut self.prefix, prefix, false).map_err(|err| {
1037 io::Error::new(
1038 err.kind(),
1039 format!("{} when setting path for {}", err, self.path_lossy()),
1040 )
1041 })?;
1042 let path = bytes2path(Cow::Borrowed(&bytes[prefixlen + 1..]))?;
1043 copy_path_into(&mut self.name, &path, false).map_err(|err| {
1044 io::Error::new(
1045 err.kind(),
1046 format!("{} when setting path for {}", err, self.path_lossy()),
1047 )
1048 })?;
1049 }
1050 Ok(())
1051 }
1052
1053 pub fn username_bytes(&self) -> &[u8] {
1055 truncate(&self.uname)
1056 }
1057
1058 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
1060 copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1061 io::Error::new(
1062 err.kind(),
1063 format!("{} when setting username for {}", err, self.path_lossy()),
1064 )
1065 })
1066 }
1067
1068 pub fn groupname_bytes(&self) -> &[u8] {
1070 truncate(&self.gname)
1071 }
1072
1073 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
1075 copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1076 io::Error::new(
1077 err.kind(),
1078 format!("{} when setting groupname for {}", err, self.path_lossy()),
1079 )
1080 })
1081 }
1082
1083 pub fn device_major(&self) -> io::Result<u32> {
1085 octal_from(&self.dev_major)
1086 .map(|u| u as u32)
1087 .map_err(|err| {
1088 io::Error::new(
1089 err.kind(),
1090 format!(
1091 "{} when getting device_major for {}",
1092 err,
1093 self.path_lossy()
1094 ),
1095 )
1096 })
1097 }
1098
1099 pub fn set_device_major(&mut self, major: u32) {
1101 octal_into(&mut self.dev_major, major);
1102 }
1103
1104 pub fn device_minor(&self) -> io::Result<u32> {
1106 octal_from(&self.dev_minor)
1107 .map(|u| u as u32)
1108 .map_err(|err| {
1109 io::Error::new(
1110 err.kind(),
1111 format!(
1112 "{} when getting device_minor for {}",
1113 err,
1114 self.path_lossy()
1115 ),
1116 )
1117 })
1118 }
1119
1120 pub fn set_device_minor(&mut self, minor: u32) {
1122 octal_into(&mut self.dev_minor, minor);
1123 }
1124
1125 pub fn as_header(&self) -> &Header {
1127 unsafe { cast(self) }
1128 }
1129
1130 pub fn as_header_mut(&mut self) -> &mut Header {
1132 unsafe { cast_mut(self) }
1133 }
1134}
1135
1136impl fmt::Debug for UstarHeader {
1137 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1138 let mut f = f.debug_struct("UstarHeader");
1139 self.as_header().debug_fields(&mut f);
1140 f.finish()
1141 }
1142}
1143
1144impl GnuHeader {
1145 pub fn username_bytes(&self) -> &[u8] {
1147 truncate(&self.uname)
1148 }
1149
1150 fn fullname_lossy(&self) -> String {
1152 format!(
1153 "{}:{}",
1154 String::from_utf8_lossy(self.groupname_bytes()),
1155 String::from_utf8_lossy(self.username_bytes()),
1156 )
1157 }
1158
1159 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
1161 copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1162 io::Error::new(
1163 err.kind(),
1164 format!(
1165 "{} when setting username for {}",
1166 err,
1167 self.fullname_lossy()
1168 ),
1169 )
1170 })
1171 }
1172
1173 pub fn groupname_bytes(&self) -> &[u8] {
1175 truncate(&self.gname)
1176 }
1177
1178 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
1180 copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1181 io::Error::new(
1182 err.kind(),
1183 format!(
1184 "{} when setting groupname for {}",
1185 err,
1186 self.fullname_lossy()
1187 ),
1188 )
1189 })
1190 }
1191
1192 pub fn device_major(&self) -> io::Result<u32> {
1194 octal_from(&self.dev_major)
1195 .map(|u| u as u32)
1196 .map_err(|err| {
1197 io::Error::new(
1198 err.kind(),
1199 format!(
1200 "{} when getting device_major for {}",
1201 err,
1202 self.fullname_lossy()
1203 ),
1204 )
1205 })
1206 }
1207
1208 pub fn set_device_major(&mut self, major: u32) {
1210 octal_into(&mut self.dev_major, major);
1211 }
1212
1213 pub fn device_minor(&self) -> io::Result<u32> {
1215 octal_from(&self.dev_minor)
1216 .map(|u| u as u32)
1217 .map_err(|err| {
1218 io::Error::new(
1219 err.kind(),
1220 format!(
1221 "{} when getting device_minor for {}",
1222 err,
1223 self.fullname_lossy()
1224 ),
1225 )
1226 })
1227 }
1228
1229 pub fn set_device_minor(&mut self, minor: u32) {
1231 octal_into(&mut self.dev_minor, minor);
1232 }
1233
1234 pub fn atime(&self) -> io::Result<u64> {
1236 num_field_wrapper_from(&self.atime).map_err(|err| {
1237 io::Error::new(
1238 err.kind(),
1239 format!("{} when getting atime for {}", err, self.fullname_lossy()),
1240 )
1241 })
1242 }
1243
1244 pub fn set_atime(&mut self, atime: u64) {
1249 num_field_wrapper_into(&mut self.atime, atime);
1250 }
1251
1252 pub fn ctime(&self) -> io::Result<u64> {
1254 num_field_wrapper_from(&self.ctime).map_err(|err| {
1255 io::Error::new(
1256 err.kind(),
1257 format!("{} when getting ctime for {}", err, self.fullname_lossy()),
1258 )
1259 })
1260 }
1261
1262 pub fn set_ctime(&mut self, ctime: u64) {
1267 num_field_wrapper_into(&mut self.ctime, ctime);
1268 }
1269
1270 pub fn real_size(&self) -> io::Result<u64> {
1275 num_field_wrapper_from(&self.realsize).map_err(|err| {
1276 io::Error::new(
1277 err.kind(),
1278 format!(
1279 "{} when getting real_size for {}",
1280 err,
1281 self.fullname_lossy()
1282 ),
1283 )
1284 })
1285 }
1286
1287 pub fn set_real_size(&mut self, real_size: u64) {
1289 num_field_wrapper_into(&mut self.realsize, real_size);
1290 }
1291
1292 pub fn is_extended(&self) -> bool {
1298 self.isextended[0] == 1
1299 }
1300
1301 pub fn set_is_extended(&mut self, is_extended: bool) {
1307 self.isextended[0] = if is_extended { 1 } else { 0 };
1308 }
1309
1310 pub fn as_header(&self) -> &Header {
1312 unsafe { cast(self) }
1313 }
1314
1315 pub fn as_header_mut(&mut self) -> &mut Header {
1317 unsafe { cast_mut(self) }
1318 }
1319}
1320
1321impl fmt::Debug for GnuHeader {
1322 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1323 let mut f = f.debug_struct("GnuHeader");
1324 self.as_header().debug_fields(&mut f);
1325 if let Ok(atime) = self.atime() {
1326 f.field("atime", &atime);
1327 }
1328 if let Ok(ctime) = self.ctime() {
1329 f.field("ctime", &ctime);
1330 }
1331 f.field("is_extended", &self.is_extended())
1332 .field("sparse", &DebugSparseHeaders(&self.sparse))
1333 .finish()
1334 }
1335}
1336
1337struct DebugSparseHeaders<'a>(&'a [GnuSparseHeader]);
1338
1339impl<'a> fmt::Debug for DebugSparseHeaders<'a> {
1340 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1341 let mut f = f.debug_list();
1342 for header in self.0 {
1343 if !header.is_empty() {
1344 f.entry(header);
1345 }
1346 }
1347 f.finish()
1348 }
1349}
1350
1351impl GnuSparseHeader {
1352 pub fn is_empty(&self) -> bool {
1354 self.offset[0] == 0 || self.numbytes[0] == 0
1355 }
1356
1357 pub fn offset(&self) -> io::Result<u64> {
1361 num_field_wrapper_from(&self.offset).map_err(|err| {
1362 io::Error::new(
1363 err.kind(),
1364 format!("{} when getting offset from sparse header", err),
1365 )
1366 })
1367 }
1368
1369 pub fn set_offset(&mut self, offset: u64) {
1371 num_field_wrapper_into(&mut self.offset, offset);
1372 }
1373
1374 pub fn length(&self) -> io::Result<u64> {
1378 num_field_wrapper_from(&self.numbytes).map_err(|err| {
1379 io::Error::new(
1380 err.kind(),
1381 format!("{} when getting length from sparse header", err),
1382 )
1383 })
1384 }
1385
1386 pub fn set_length(&mut self, length: u64) {
1388 num_field_wrapper_into(&mut self.numbytes, length);
1389 }
1390}
1391
1392impl fmt::Debug for GnuSparseHeader {
1393 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1394 let mut f = f.debug_struct("GnuSparseHeader");
1395 if let Ok(offset) = self.offset() {
1396 f.field("offset", &offset);
1397 }
1398 if let Ok(length) = self.length() {
1399 f.field("length", &length);
1400 }
1401 f.finish()
1402 }
1403}
1404
1405impl GnuExtSparseHeader {
1406 pub fn new() -> GnuExtSparseHeader {
1408 unsafe { mem::zeroed() }
1409 }
1410
1411 pub fn as_bytes(&self) -> &[u8; BLOCK_SIZE as usize] {
1413 debug_assert_eq!(mem::size_of_val(self), BLOCK_SIZE as usize);
1414 unsafe { mem::transmute(self) }
1415 }
1416
1417 pub fn as_mut_bytes(&mut self) -> &mut [u8; BLOCK_SIZE as usize] {
1419 debug_assert_eq!(mem::size_of_val(self), BLOCK_SIZE as usize);
1420 unsafe { mem::transmute(self) }
1421 }
1422
1423 pub fn sparse(&self) -> &[GnuSparseHeader; 21] {
1428 &self.sparse
1429 }
1430
1431 pub fn sparse_mut(&mut self) -> &mut [GnuSparseHeader; 21] {
1433 &mut self.sparse
1434 }
1435
1436 pub fn is_extended(&self) -> bool {
1438 self.isextended[0] == 1
1439 }
1440
1441 pub fn set_is_extended(&mut self, is_extended: bool) {
1443 self.isextended[0] = if is_extended { 1 } else { 0 };
1444 }
1445}
1446
1447impl Default for GnuExtSparseHeader {
1448 fn default() -> Self {
1449 Self::new()
1450 }
1451}
1452
1453fn octal_from(slice: &[u8]) -> io::Result<u64> {
1454 let trun = truncate(slice);
1455 let num = match str::from_utf8(trun) {
1456 Ok(n) => n,
1457 Err(_) => {
1458 return Err(other(&format!(
1459 "numeric field did not have utf-8 text: {}",
1460 String::from_utf8_lossy(trun)
1461 )));
1462 }
1463 };
1464 match u64::from_str_radix(num.trim(), 8) {
1465 Ok(n) => Ok(n),
1466 Err(_) => Err(other(&format!("numeric field was not a number: {}", num))),
1467 }
1468}
1469
1470fn octal_into<T: fmt::Octal>(dst: &mut [u8], val: T) {
1471 let o = format!("{:o}", val);
1472 let value = once(b'\0').chain(o.bytes().rev().chain(repeat(b'0')));
1473 for (slot, value) in dst.iter_mut().rev().zip(value) {
1474 *slot = value;
1475 }
1476}
1477
1478fn num_field_wrapper_into(dst: &mut [u8], src: u64) {
1481 if src >= 8589934592 || (src >= 2097152 && dst.len() == 8) {
1482 numeric_extended_into(dst, src);
1483 } else {
1484 octal_into(dst, src);
1485 }
1486}
1487
1488fn num_field_wrapper_from(src: &[u8]) -> io::Result<u64> {
1491 if src[0] & 0x80 != 0 {
1492 Ok(numeric_extended_from(src))
1493 } else {
1494 octal_from(src)
1495 }
1496}
1497
1498fn numeric_extended_into(dst: &mut [u8], src: u64) {
1503 let len: usize = dst.len();
1504 for (slot, val) in dst.iter_mut().zip(
1505 repeat(0)
1506 .take(len - 8) .chain((0..8).rev().map(|x| ((src >> (8 * x)) & 0xff) as u8)),
1508 ) {
1509 *slot = val;
1510 }
1511 dst[0] |= 0x80;
1512}
1513
1514fn numeric_extended_from(src: &[u8]) -> u64 {
1515 let mut dst: u64 = 0;
1516 let mut b_to_skip = 1;
1517 if src.len() == 8 {
1518 dst = (src[0] ^ 0x80) as u64;
1520 } else {
1521 b_to_skip = src.len() - 8;
1523 }
1524 for byte in src.iter().skip(b_to_skip) {
1525 dst <<= 8;
1526 dst |= *byte as u64;
1527 }
1528 dst
1529}
1530
1531fn truncate(slice: &[u8]) -> &[u8] {
1532 match slice.iter().position(|i| *i == 0) {
1533 Some(i) => &slice[..i],
1534 None => slice,
1535 }
1536}
1537
1538fn copy_into(slot: &mut [u8], bytes: &[u8]) -> io::Result<()> {
1541 if bytes.len() > slot.len() {
1542 Err(other("provided value is too long"))
1543 } else if bytes.contains(&0) {
1544 Err(other("provided value contains a nul byte"))
1545 } else {
1546 for (slot, val) in slot.iter_mut().zip(bytes.iter().chain(Some(&0))) {
1547 *slot = *val;
1548 }
1549 Ok(())
1550 }
1551}
1552
1553fn copy_path_into_inner(
1554 mut slot: &mut [u8],
1555 path: &Path,
1556 is_link_name: bool,
1557 is_truncated_gnu_long_path: bool,
1558) -> io::Result<()> {
1559 let mut emitted = false;
1560 let mut needs_slash = false;
1561 let mut iter = path.components().peekable();
1562 while let Some(component) = iter.next() {
1563 let bytes = path2bytes(Path::new(component.as_os_str()))?;
1564 match (component, is_link_name) {
1565 (Component::Prefix(..), false) | (Component::RootDir, false) => {
1566 return Err(other("paths in archives must be relative"));
1567 }
1568 (Component::ParentDir, false) => {
1569 if !is_truncated_gnu_long_path || iter.peek().is_some() {
1573 return Err(other("paths in archives must not have `..`"));
1574 }
1575 }
1576 (Component::CurDir, false) if path.components().count() == 1 => {}
1578 (Component::CurDir, false) => continue,
1579 (Component::Normal(_), _) | (_, true) => {}
1580 };
1581 if needs_slash {
1582 copy(&mut slot, b"/")?;
1583 }
1584 if bytes.contains(&b'/') {
1585 if let Component::Normal(..) = component {
1586 return Err(other("path component in archive cannot contain `/`"));
1587 }
1588 }
1589 copy(&mut slot, &bytes)?;
1590 if &*bytes != b"/" {
1591 needs_slash = true;
1592 }
1593 emitted = true;
1594 }
1595 if !emitted {
1596 return Err(other("paths in archives must have at least one component"));
1597 }
1598 if ends_with_slash(path) {
1599 copy(&mut slot, b"/")?;
1600 }
1601 return Ok(());
1602
1603 fn copy(slot: &mut &mut [u8], bytes: &[u8]) -> io::Result<()> {
1604 copy_into(slot, bytes)?;
1605 let tmp = mem::take(slot);
1606 *slot = &mut tmp[bytes.len()..];
1607 Ok(())
1608 }
1609}
1610
1611fn copy_path_into(slot: &mut [u8], path: &Path, is_link_name: bool) -> io::Result<()> {
1620 copy_path_into_inner(slot, path, is_link_name, false)
1621}
1622
1623fn copy_path_into_gnu_long(slot: &mut [u8], path: &Path, is_link_name: bool) -> io::Result<()> {
1634 copy_path_into_inner(slot, path, is_link_name, true)
1635}
1636
1637#[cfg(target_arch = "wasm32")]
1638fn ends_with_slash(p: &Path) -> bool {
1639 p.to_string_lossy().ends_with('/')
1640}
1641
1642#[cfg(windows)]
1643fn ends_with_slash(p: &Path) -> bool {
1644 let last = p.as_os_str().encode_wide().last();
1645 last == Some(b'/' as u16) || last == Some(b'\\' as u16)
1646}
1647
1648#[cfg(all(unix, not(target_arch = "wasm32")))]
1649fn ends_with_slash(p: &Path) -> bool {
1650 p.as_os_str().as_bytes().ends_with(b"/")
1651}
1652
1653#[cfg(any(windows, target_arch = "wasm32"))]
1654pub fn path2bytes(p: &Path) -> io::Result<Cow<'_, [u8]>> {
1655 p.as_os_str()
1656 .to_str()
1657 .map(|s| s.as_bytes())
1658 .ok_or_else(|| other(&format!("path {} was not valid Unicode", p.display())))
1659 .map(|bytes| {
1660 if bytes.contains(&b'\\') {
1661 let mut bytes = bytes.to_owned();
1663 for b in &mut bytes {
1664 if *b == b'\\' {
1665 *b = b'/';
1666 }
1667 }
1668 Cow::Owned(bytes)
1669 } else {
1670 Cow::Borrowed(bytes)
1671 }
1672 })
1673}
1674
1675#[cfg(all(unix, not(target_arch = "wasm32")))]
1676pub fn path2bytes(p: &Path) -> io::Result<Cow<'_, [u8]>> {
1678 Ok(Cow::Borrowed(p.as_os_str().as_bytes()))
1679}
1680
1681#[cfg(windows)]
1682pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1685 return match bytes {
1686 Cow::Borrowed(bytes) => {
1687 let s = str::from_utf8(bytes).map_err(|_| not_unicode(bytes))?;
1688 Ok(Cow::Borrowed(Path::new(s)))
1689 }
1690 Cow::Owned(bytes) => {
1691 let s = String::from_utf8(bytes).map_err(|uerr| not_unicode(&uerr.into_bytes()))?;
1692 Ok(Cow::Owned(PathBuf::from(s)))
1693 }
1694 };
1695
1696 fn not_unicode(v: &[u8]) -> io::Error {
1697 other(&format!(
1698 "only Unicode paths are supported on Windows: {}",
1699 String::from_utf8_lossy(v)
1700 ))
1701 }
1702}
1703
1704#[cfg(all(unix, not(target_arch = "wasm32")))]
1705pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1707 use std::ffi::{OsStr, OsString};
1708
1709 Ok(match bytes {
1710 Cow::Borrowed(bytes) => Cow::Borrowed(Path::new(OsStr::from_bytes(bytes))),
1711 Cow::Owned(bytes) => Cow::Owned(PathBuf::from(OsString::from_vec(bytes))),
1712 })
1713}
1714
1715#[cfg(target_arch = "wasm32")]
1716pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1717 Ok(match bytes {
1718 Cow::Borrowed(bytes) => {
1719 Cow::Borrowed(Path::new(str::from_utf8(bytes).map_err(invalid_utf8)?))
1720 }
1721 Cow::Owned(bytes) => Cow::Owned(PathBuf::from(
1722 String::from_utf8(bytes).map_err(invalid_utf8)?,
1723 )),
1724 })
1725}
1726
1727#[cfg(target_arch = "wasm32")]
1728fn invalid_utf8<T>(_: T) -> io::Error {
1729 io::Error::new(io::ErrorKind::InvalidData, "Invalid utf-8")
1730}