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, false)
391 }
392
393 pub fn set_path_absolute<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
397 self.set_path_inner(p.as_ref(), false, true)
398 }
399
400 pub(crate) fn set_truncated_path_for_gnu_header<P: AsRef<Path>>(
404 &mut self,
405 p: P,
406 allow_absolute: bool,
407 ) -> io::Result<()> {
408 self.set_path_inner(p.as_ref(), true, allow_absolute)
409 }
410
411 fn set_path_inner(
412 &mut self,
413 path: &Path,
414 is_truncated_gnu_long_path: bool,
415 allow_absolute: bool,
416 ) -> io::Result<()> {
417 if let Some(ustar) = self.as_ustar_mut() {
418 return if allow_absolute {
419 ustar.set_path_absolute(path)
420 } else {
421 ustar.set_path(path)
422 };
423 }
424 if is_truncated_gnu_long_path {
425 copy_path_into_gnu_long(&mut self.as_old_mut().name, path, false, allow_absolute)
426 } else {
427 copy_path_into(&mut self.as_old_mut().name, path, false, allow_absolute)
428 }
429 .map_err(|err| {
430 io::Error::new(
431 err.kind(),
432 format!("{} when setting path for {}", err, self.path_lossy()),
433 )
434 })
435 }
436
437 pub fn link_name(&self) -> io::Result<Option<Cow<'_, Path>>> {
446 match self.link_name_bytes() {
447 Some(bytes) => bytes2path(bytes).map(Some),
448 None => Ok(None),
449 }
450 }
451
452 pub fn link_name_bytes(&self) -> Option<Cow<'_, [u8]>> {
460 let old = self.as_old();
461 if old.linkname[0] != 0 {
462 Some(Cow::Borrowed(truncate(&old.linkname)))
463 } else {
464 None
465 }
466 }
467
468 pub fn set_link_name<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
477 self._set_link_name(p.as_ref())
478 }
479
480 fn _set_link_name(&mut self, path: &Path) -> io::Result<()> {
481 copy_path_into(&mut self.as_old_mut().linkname, path, true, true).map_err(|err| {
482 io::Error::new(
483 err.kind(),
484 format!("{} when setting link name for {}", err, self.path_lossy()),
485 )
486 })
487 }
488
489 pub fn set_link_name_literal<P: AsRef<[u8]>>(&mut self, p: P) -> io::Result<()> {
494 self._set_link_name_literal(p.as_ref())
495 }
496
497 fn _set_link_name_literal(&mut self, bytes: &[u8]) -> io::Result<()> {
498 copy_into(&mut self.as_old_mut().linkname, bytes)
499 }
500
501 pub fn mode(&self) -> io::Result<u32> {
505 octal_from(&self.as_old().mode)
506 .map(|u| u as u32)
507 .map_err(|err| {
508 io::Error::new(
509 err.kind(),
510 format!("{} when getting mode for {}", err, self.path_lossy()),
511 )
512 })
513 }
514
515 pub fn set_mode(&mut self, mode: u32) {
517 octal_into(&mut self.as_old_mut().mode, mode);
518 }
519
520 pub fn uid(&self) -> io::Result<u64> {
524 num_field_wrapper_from(&self.as_old().uid).map_err(|err| {
525 io::Error::new(
526 err.kind(),
527 format!("{} when getting uid for {}", err, self.path_lossy()),
528 )
529 })
530 }
531
532 pub fn set_uid(&mut self, uid: u64) {
534 num_field_wrapper_into(&mut self.as_old_mut().uid, uid);
535 }
536
537 pub fn gid(&self) -> io::Result<u64> {
539 num_field_wrapper_from(&self.as_old().gid).map_err(|err| {
540 io::Error::new(
541 err.kind(),
542 format!("{} when getting gid for {}", err, self.path_lossy()),
543 )
544 })
545 }
546
547 pub fn set_gid(&mut self, gid: u64) {
549 num_field_wrapper_into(&mut self.as_old_mut().gid, gid);
550 }
551
552 pub fn mtime(&self) -> io::Result<u64> {
554 num_field_wrapper_from(&self.as_old().mtime).map_err(|err| {
555 io::Error::new(
556 err.kind(),
557 format!("{} when getting mtime for {}", err, self.path_lossy()),
558 )
559 })
560 }
561
562 pub fn set_mtime(&mut self, mtime: u64) {
567 num_field_wrapper_into(&mut self.as_old_mut().mtime, mtime);
568 }
569
570 pub fn username(&self) -> Result<Option<&str>, str::Utf8Error> {
577 match self.username_bytes() {
578 Some(bytes) => str::from_utf8(bytes).map(Some),
579 None => Ok(None),
580 }
581 }
582
583 pub fn username_bytes(&self) -> Option<&[u8]> {
588 if let Some(ustar) = self.as_ustar() {
589 Some(ustar.username_bytes())
590 } else if let Some(gnu) = self.as_gnu() {
591 Some(gnu.username_bytes())
592 } else {
593 None
594 }
595 }
596
597 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
602 if let Some(ustar) = self.as_ustar_mut() {
603 return ustar.set_username(name);
604 }
605 if let Some(gnu) = self.as_gnu_mut() {
606 gnu.set_username(name)
607 } else {
608 Err(other("not a ustar or gnu archive, cannot set username"))
609 }
610 }
611
612 pub fn groupname(&self) -> Result<Option<&str>, str::Utf8Error> {
619 match self.groupname_bytes() {
620 Some(bytes) => str::from_utf8(bytes).map(Some),
621 None => Ok(None),
622 }
623 }
624
625 pub fn groupname_bytes(&self) -> Option<&[u8]> {
630 if let Some(ustar) = self.as_ustar() {
631 Some(ustar.groupname_bytes())
632 } else if let Some(gnu) = self.as_gnu() {
633 Some(gnu.groupname_bytes())
634 } else {
635 None
636 }
637 }
638
639 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
644 if let Some(ustar) = self.as_ustar_mut() {
645 return ustar.set_groupname(name);
646 }
647 if let Some(gnu) = self.as_gnu_mut() {
648 gnu.set_groupname(name)
649 } else {
650 Err(other("not a ustar or gnu archive, cannot set groupname"))
651 }
652 }
653
654 pub fn device_major(&self) -> io::Result<Option<u32>> {
662 if let Some(ustar) = self.as_ustar() {
663 ustar.device_major().map(Some)
664 } else if let Some(gnu) = self.as_gnu() {
665 gnu.device_major().map(Some)
666 } else {
667 Ok(None)
668 }
669 }
670
671 pub fn set_device_major(&mut self, major: u32) -> io::Result<()> {
676 if let Some(ustar) = self.as_ustar_mut() {
677 ustar.set_device_major(major);
678 Ok(())
679 } else if let Some(gnu) = self.as_gnu_mut() {
680 gnu.set_device_major(major);
681 Ok(())
682 } else {
683 Err(other("not a ustar or gnu archive, cannot set dev_major"))
684 }
685 }
686
687 pub fn device_minor(&self) -> io::Result<Option<u32>> {
695 if let Some(ustar) = self.as_ustar() {
696 ustar.device_minor().map(Some)
697 } else if let Some(gnu) = self.as_gnu() {
698 gnu.device_minor().map(Some)
699 } else {
700 Ok(None)
701 }
702 }
703
704 pub fn set_device_minor(&mut self, minor: u32) -> io::Result<()> {
709 if let Some(ustar) = self.as_ustar_mut() {
710 ustar.set_device_minor(minor);
711 Ok(())
712 } else if let Some(gnu) = self.as_gnu_mut() {
713 gnu.set_device_minor(minor);
714 Ok(())
715 } else {
716 Err(other("not a ustar or gnu archive, cannot set dev_minor"))
717 }
718 }
719
720 pub fn entry_type(&self) -> EntryType {
722 EntryType::new(self.as_old().linkflag[0])
723 }
724
725 pub fn set_entry_type(&mut self, ty: EntryType) {
727 self.as_old_mut().linkflag = [ty.as_byte()];
728 }
729
730 pub fn cksum(&self) -> io::Result<u32> {
734 octal_from(&self.as_old().cksum)
735 .map(|u| u as u32)
736 .map_err(|err| {
737 io::Error::new(
738 err.kind(),
739 format!("{} when getting cksum for {}", err, self.path_lossy()),
740 )
741 })
742 }
743
744 pub fn set_cksum(&mut self) {
747 let cksum = self.calculate_cksum();
748 octal_into(&mut self.as_old_mut().cksum, cksum);
749 }
750
751 fn calculate_cksum(&self) -> u32 {
752 let old = self.as_old();
753 let start = old as *const _ as usize;
754 let cksum_start = old.cksum.as_ptr() as *const _ as usize;
755 let offset = cksum_start - start;
756 let len = old.cksum.len();
757 self.bytes[0..offset]
758 .iter()
759 .chain(repeat(&b' ').take(len))
760 .chain(&self.bytes[offset + len..])
761 .fold(0, |a, b| a + (*b as u32))
762 }
763
764 fn fill_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
765 self.fill_platform_from(meta, mode);
766 self.set_size(if meta.is_dir() || meta.file_type().is_symlink() {
768 0
769 } else {
770 meta.len()
771 });
772 if let Some(ustar) = self.as_ustar_mut() {
773 ustar.set_device_major(0);
774 ustar.set_device_minor(0);
775 }
776 if let Some(gnu) = self.as_gnu_mut() {
777 gnu.set_device_major(0);
778 gnu.set_device_minor(0);
779 }
780 }
781
782 #[cfg(target_arch = "wasm32")]
783 #[allow(unused_variables)]
784 fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
785 unimplemented!();
786 }
787
788 #[cfg(all(unix, not(target_arch = "wasm32")))]
789 fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
790 match mode {
791 HeaderMode::Complete => {
792 self.set_mtime(meta.mtime() as u64);
793 self.set_uid(meta.uid() as u64);
794 self.set_gid(meta.gid() as u64);
795 self.set_mode(meta.mode());
796 }
797 HeaderMode::Deterministic => {
798 self.set_mtime(DETERMINISTIC_TIMESTAMP);
803
804 self.set_uid(0);
805 self.set_gid(0);
806
807 let fs_mode = if meta.is_dir() || (0o100 & meta.mode() == 0o100) {
809 0o755
810 } else {
811 0o644
812 };
813 self.set_mode(fs_mode);
814 }
815 }
816
817 self.set_entry_type(entry_type(meta.mode()));
828
829 fn entry_type(mode: u32) -> EntryType {
830 match mode as libc::mode_t & libc::S_IFMT {
831 libc::S_IFREG => EntryType::file(),
832 libc::S_IFLNK => EntryType::symlink(),
833 libc::S_IFCHR => EntryType::character_special(),
834 libc::S_IFBLK => EntryType::block_special(),
835 libc::S_IFDIR => EntryType::dir(),
836 libc::S_IFIFO => EntryType::fifo(),
837 _ => EntryType::new(b' '),
838 }
839 }
840 }
841
842 #[cfg(windows)]
843 fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
844 match mode {
846 HeaderMode::Complete => {
847 self.set_uid(0);
848 self.set_gid(0);
849 let mtime = (meta.last_write_time() / (1_000_000_000 / 100)) - 11644473600;
854 self.set_mtime(mtime);
855 let fs_mode = {
856 const FILE_ATTRIBUTE_READONLY: u32 = 0x00000001;
857 let readonly = meta.file_attributes() & FILE_ATTRIBUTE_READONLY;
858 match (meta.is_dir(), readonly != 0) {
859 (true, false) => 0o755,
860 (true, true) => 0o555,
861 (false, false) => 0o644,
862 (false, true) => 0o444,
863 }
864 };
865 self.set_mode(fs_mode);
866 }
867 HeaderMode::Deterministic => {
868 self.set_uid(0);
869 self.set_gid(0);
870 self.set_mtime(DETERMINISTIC_TIMESTAMP); let fs_mode = if meta.is_dir() { 0o755 } else { 0o644 };
872 self.set_mode(fs_mode);
873 }
874 }
875
876 let ft = meta.file_type();
877 self.set_entry_type(if ft.is_dir() {
878 EntryType::dir()
879 } else if ft.is_file() {
880 EntryType::file()
881 } else if ft.is_symlink() {
882 EntryType::symlink()
883 } else {
884 EntryType::new(b' ')
885 });
886 }
887
888 fn debug_fields(&self, b: &mut fmt::DebugStruct) {
889 if let Ok(entry_size) = self.entry_size() {
890 b.field("entry_size", &entry_size);
891 }
892 if let Ok(size) = self.size() {
893 b.field("size", &size);
894 }
895 if let Ok(path) = self.path() {
896 b.field("path", &path);
897 }
898 if let Ok(link_name) = self.link_name() {
899 b.field("link_name", &link_name);
900 }
901 if let Ok(mode) = self.mode() {
902 b.field("mode", &DebugAsOctal(mode));
903 }
904 if let Ok(uid) = self.uid() {
905 b.field("uid", &uid);
906 }
907 if let Ok(gid) = self.gid() {
908 b.field("gid", &gid);
909 }
910 if let Ok(mtime) = self.mtime() {
911 b.field("mtime", &mtime);
912 }
913 if let Ok(username) = self.username() {
914 b.field("username", &username);
915 }
916 if let Ok(groupname) = self.groupname() {
917 b.field("groupname", &groupname);
918 }
919 if let Ok(device_major) = self.device_major() {
920 b.field("device_major", &device_major);
921 }
922 if let Ok(device_minor) = self.device_minor() {
923 b.field("device_minor", &device_minor);
924 }
925 if let Ok(cksum) = self.cksum() {
926 b.field("cksum", &cksum);
927 b.field("cksum_valid", &(cksum == self.calculate_cksum()));
928 }
929 }
930}
931
932struct DebugAsOctal<T>(T);
933
934impl<T: fmt::Octal> fmt::Debug for DebugAsOctal<T> {
935 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
936 fmt::Octal::fmt(&self.0, f)
937 }
938}
939
940unsafe fn cast<T, U>(a: &T) -> &U {
941 assert_eq!(mem::size_of_val(a), mem::size_of::<U>());
942 assert_eq!(mem::align_of_val(a), mem::align_of::<U>());
943 &*(a as *const T as *const U)
944}
945
946unsafe fn cast_mut<T, U>(a: &mut T) -> &mut U {
947 assert_eq!(mem::size_of_val(a), mem::size_of::<U>());
948 assert_eq!(mem::align_of_val(a), mem::align_of::<U>());
949 &mut *(a as *mut T as *mut U)
950}
951
952impl Clone for Header {
953 fn clone(&self) -> Header {
954 Header { bytes: self.bytes }
955 }
956}
957
958impl fmt::Debug for Header {
959 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
960 if let Some(me) = self.as_ustar() {
961 me.fmt(f)
962 } else if let Some(me) = self.as_gnu() {
963 me.fmt(f)
964 } else {
965 self.as_old().fmt(f)
966 }
967 }
968}
969
970impl OldHeader {
971 pub fn as_header(&self) -> &Header {
973 unsafe { cast(self) }
974 }
975
976 pub fn as_header_mut(&mut self) -> &mut Header {
978 unsafe { cast_mut(self) }
979 }
980}
981
982impl fmt::Debug for OldHeader {
983 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
984 let mut f = f.debug_struct("OldHeader");
985 self.as_header().debug_fields(&mut f);
986 f.finish()
987 }
988}
989
990impl UstarHeader {
991 pub fn path_bytes(&self) -> Cow<'_, [u8]> {
993 if self.prefix[0] == 0 && !self.name.contains(&b'\\') {
994 Cow::Borrowed(truncate(&self.name))
995 } else {
996 let mut bytes = Vec::new();
997 let prefix = truncate(&self.prefix);
998 if !prefix.is_empty() {
999 bytes.extend_from_slice(prefix);
1000 bytes.push(b'/');
1001 }
1002 bytes.extend_from_slice(truncate(&self.name));
1003 Cow::Owned(bytes)
1004 }
1005 }
1006
1007 fn path_lossy(&self) -> String {
1009 String::from_utf8_lossy(&self.path_bytes()).to_string()
1010 }
1011
1012 pub fn set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
1014 self._set_path(p.as_ref(), false)
1015 }
1016
1017 pub fn set_path_absolute<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
1019 self._set_path(p.as_ref(), true)
1020 }
1021
1022 fn _set_path(&mut self, path: &Path, allow_absolute: bool) -> io::Result<()> {
1023 let bytes = path2bytes(path)?;
1032 let (maxnamelen, maxprefixlen) = (self.name.len(), self.prefix.len());
1033 if bytes.len() <= maxnamelen {
1034 copy_path_into(&mut self.name, path, false, allow_absolute).map_err(|err| {
1035 io::Error::new(
1036 err.kind(),
1037 format!("{} when setting path for {}", err, self.path_lossy()),
1038 )
1039 })?;
1040 } else {
1041 let mut prefix = path;
1042 let mut prefixlen;
1043 loop {
1044 match prefix.parent() {
1045 Some(parent) => prefix = parent,
1046 None => {
1047 return Err(other(&format!(
1048 "path cannot be split to be inserted into archive: {}",
1049 path.display()
1050 )));
1051 }
1052 }
1053 prefixlen = path2bytes(prefix)?.len();
1054 if prefixlen <= maxprefixlen {
1055 break;
1056 }
1057 }
1058 copy_path_into(&mut self.prefix, prefix, false, allow_absolute).map_err(|err| {
1059 io::Error::new(
1060 err.kind(),
1061 format!("{} when setting path for {}", err, self.path_lossy()),
1062 )
1063 })?;
1064 let path = bytes2path(Cow::Borrowed(&bytes[prefixlen + 1..]))?;
1065 copy_path_into(&mut self.name, &path, false, allow_absolute).map_err(|err| {
1066 io::Error::new(
1067 err.kind(),
1068 format!("{} when setting path for {}", err, self.path_lossy()),
1069 )
1070 })?;
1071 }
1072 Ok(())
1073 }
1074
1075 pub fn username_bytes(&self) -> &[u8] {
1077 truncate(&self.uname)
1078 }
1079
1080 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
1082 copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1083 io::Error::new(
1084 err.kind(),
1085 format!("{} when setting username for {}", err, self.path_lossy()),
1086 )
1087 })
1088 }
1089
1090 pub fn groupname_bytes(&self) -> &[u8] {
1092 truncate(&self.gname)
1093 }
1094
1095 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
1097 copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1098 io::Error::new(
1099 err.kind(),
1100 format!("{} when setting groupname for {}", err, self.path_lossy()),
1101 )
1102 })
1103 }
1104
1105 pub fn device_major(&self) -> io::Result<u32> {
1107 octal_from(&self.dev_major)
1108 .map(|u| u as u32)
1109 .map_err(|err| {
1110 io::Error::new(
1111 err.kind(),
1112 format!(
1113 "{} when getting device_major for {}",
1114 err,
1115 self.path_lossy()
1116 ),
1117 )
1118 })
1119 }
1120
1121 pub fn set_device_major(&mut self, major: u32) {
1123 octal_into(&mut self.dev_major, major);
1124 }
1125
1126 pub fn device_minor(&self) -> io::Result<u32> {
1128 octal_from(&self.dev_minor)
1129 .map(|u| u as u32)
1130 .map_err(|err| {
1131 io::Error::new(
1132 err.kind(),
1133 format!(
1134 "{} when getting device_minor for {}",
1135 err,
1136 self.path_lossy()
1137 ),
1138 )
1139 })
1140 }
1141
1142 pub fn set_device_minor(&mut self, minor: u32) {
1144 octal_into(&mut self.dev_minor, minor);
1145 }
1146
1147 pub fn as_header(&self) -> &Header {
1149 unsafe { cast(self) }
1150 }
1151
1152 pub fn as_header_mut(&mut self) -> &mut Header {
1154 unsafe { cast_mut(self) }
1155 }
1156}
1157
1158impl fmt::Debug for UstarHeader {
1159 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1160 let mut f = f.debug_struct("UstarHeader");
1161 self.as_header().debug_fields(&mut f);
1162 f.finish()
1163 }
1164}
1165
1166impl GnuHeader {
1167 pub fn username_bytes(&self) -> &[u8] {
1169 truncate(&self.uname)
1170 }
1171
1172 fn fullname_lossy(&self) -> String {
1174 format!(
1175 "{}:{}",
1176 String::from_utf8_lossy(self.groupname_bytes()),
1177 String::from_utf8_lossy(self.username_bytes()),
1178 )
1179 }
1180
1181 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
1183 copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1184 io::Error::new(
1185 err.kind(),
1186 format!(
1187 "{} when setting username for {}",
1188 err,
1189 self.fullname_lossy()
1190 ),
1191 )
1192 })
1193 }
1194
1195 pub fn groupname_bytes(&self) -> &[u8] {
1197 truncate(&self.gname)
1198 }
1199
1200 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
1202 copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1203 io::Error::new(
1204 err.kind(),
1205 format!(
1206 "{} when setting groupname for {}",
1207 err,
1208 self.fullname_lossy()
1209 ),
1210 )
1211 })
1212 }
1213
1214 pub fn device_major(&self) -> io::Result<u32> {
1216 octal_from(&self.dev_major)
1217 .map(|u| u as u32)
1218 .map_err(|err| {
1219 io::Error::new(
1220 err.kind(),
1221 format!(
1222 "{} when getting device_major for {}",
1223 err,
1224 self.fullname_lossy()
1225 ),
1226 )
1227 })
1228 }
1229
1230 pub fn set_device_major(&mut self, major: u32) {
1232 octal_into(&mut self.dev_major, major);
1233 }
1234
1235 pub fn device_minor(&self) -> io::Result<u32> {
1237 octal_from(&self.dev_minor)
1238 .map(|u| u as u32)
1239 .map_err(|err| {
1240 io::Error::new(
1241 err.kind(),
1242 format!(
1243 "{} when getting device_minor for {}",
1244 err,
1245 self.fullname_lossy()
1246 ),
1247 )
1248 })
1249 }
1250
1251 pub fn set_device_minor(&mut self, minor: u32) {
1253 octal_into(&mut self.dev_minor, minor);
1254 }
1255
1256 pub fn atime(&self) -> io::Result<u64> {
1258 num_field_wrapper_from(&self.atime).map_err(|err| {
1259 io::Error::new(
1260 err.kind(),
1261 format!("{} when getting atime for {}", err, self.fullname_lossy()),
1262 )
1263 })
1264 }
1265
1266 pub fn set_atime(&mut self, atime: u64) {
1271 num_field_wrapper_into(&mut self.atime, atime);
1272 }
1273
1274 pub fn ctime(&self) -> io::Result<u64> {
1276 num_field_wrapper_from(&self.ctime).map_err(|err| {
1277 io::Error::new(
1278 err.kind(),
1279 format!("{} when getting ctime for {}", err, self.fullname_lossy()),
1280 )
1281 })
1282 }
1283
1284 pub fn set_ctime(&mut self, ctime: u64) {
1289 num_field_wrapper_into(&mut self.ctime, ctime);
1290 }
1291
1292 pub fn real_size(&self) -> io::Result<u64> {
1297 num_field_wrapper_from(&self.realsize).map_err(|err| {
1298 io::Error::new(
1299 err.kind(),
1300 format!(
1301 "{} when getting real_size for {}",
1302 err,
1303 self.fullname_lossy()
1304 ),
1305 )
1306 })
1307 }
1308
1309 pub fn set_real_size(&mut self, real_size: u64) {
1311 num_field_wrapper_into(&mut self.realsize, real_size);
1312 }
1313
1314 pub fn is_extended(&self) -> bool {
1320 self.isextended[0] == 1
1321 }
1322
1323 pub fn set_is_extended(&mut self, is_extended: bool) {
1329 self.isextended[0] = if is_extended { 1 } else { 0 };
1330 }
1331
1332 pub fn as_header(&self) -> &Header {
1334 unsafe { cast(self) }
1335 }
1336
1337 pub fn as_header_mut(&mut self) -> &mut Header {
1339 unsafe { cast_mut(self) }
1340 }
1341}
1342
1343impl fmt::Debug for GnuHeader {
1344 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1345 let mut f = f.debug_struct("GnuHeader");
1346 self.as_header().debug_fields(&mut f);
1347 if let Ok(atime) = self.atime() {
1348 f.field("atime", &atime);
1349 }
1350 if let Ok(ctime) = self.ctime() {
1351 f.field("ctime", &ctime);
1352 }
1353 f.field("is_extended", &self.is_extended())
1354 .field("sparse", &DebugSparseHeaders(&self.sparse))
1355 .finish()
1356 }
1357}
1358
1359struct DebugSparseHeaders<'a>(&'a [GnuSparseHeader]);
1360
1361impl<'a> fmt::Debug for DebugSparseHeaders<'a> {
1362 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1363 let mut f = f.debug_list();
1364 for header in self.0 {
1365 if !header.is_empty() {
1366 f.entry(header);
1367 }
1368 }
1369 f.finish()
1370 }
1371}
1372
1373impl GnuSparseHeader {
1374 pub fn is_empty(&self) -> bool {
1376 self.offset[0] == 0 || self.numbytes[0] == 0
1377 }
1378
1379 pub fn offset(&self) -> io::Result<u64> {
1383 num_field_wrapper_from(&self.offset).map_err(|err| {
1384 io::Error::new(
1385 err.kind(),
1386 format!("{} when getting offset from sparse header", err),
1387 )
1388 })
1389 }
1390
1391 pub fn set_offset(&mut self, offset: u64) {
1393 num_field_wrapper_into(&mut self.offset, offset);
1394 }
1395
1396 pub fn length(&self) -> io::Result<u64> {
1400 num_field_wrapper_from(&self.numbytes).map_err(|err| {
1401 io::Error::new(
1402 err.kind(),
1403 format!("{} when getting length from sparse header", err),
1404 )
1405 })
1406 }
1407
1408 pub fn set_length(&mut self, length: u64) {
1410 num_field_wrapper_into(&mut self.numbytes, length);
1411 }
1412}
1413
1414impl fmt::Debug for GnuSparseHeader {
1415 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1416 let mut f = f.debug_struct("GnuSparseHeader");
1417 if let Ok(offset) = self.offset() {
1418 f.field("offset", &offset);
1419 }
1420 if let Ok(length) = self.length() {
1421 f.field("length", &length);
1422 }
1423 f.finish()
1424 }
1425}
1426
1427impl GnuExtSparseHeader {
1428 pub fn new() -> GnuExtSparseHeader {
1430 unsafe { mem::zeroed() }
1431 }
1432
1433 pub fn as_bytes(&self) -> &[u8; BLOCK_SIZE as usize] {
1435 debug_assert_eq!(mem::size_of_val(self), BLOCK_SIZE as usize);
1436 unsafe { mem::transmute(self) }
1437 }
1438
1439 pub fn as_mut_bytes(&mut self) -> &mut [u8; BLOCK_SIZE as usize] {
1441 debug_assert_eq!(mem::size_of_val(self), BLOCK_SIZE as usize);
1442 unsafe { mem::transmute(self) }
1443 }
1444
1445 pub fn sparse(&self) -> &[GnuSparseHeader; 21] {
1450 &self.sparse
1451 }
1452
1453 pub fn sparse_mut(&mut self) -> &mut [GnuSparseHeader; 21] {
1455 &mut self.sparse
1456 }
1457
1458 pub fn is_extended(&self) -> bool {
1460 self.isextended[0] == 1
1461 }
1462
1463 pub fn set_is_extended(&mut self, is_extended: bool) {
1465 self.isextended[0] = if is_extended { 1 } else { 0 };
1466 }
1467}
1468
1469impl Default for GnuExtSparseHeader {
1470 fn default() -> Self {
1471 Self::new()
1472 }
1473}
1474
1475fn octal_from(slice: &[u8]) -> io::Result<u64> {
1476 let trun = truncate(slice);
1477 let num = match str::from_utf8(trun) {
1478 Ok(n) => n,
1479 Err(_) => {
1480 return Err(other(&format!(
1481 "numeric field did not have utf-8 text: {}",
1482 String::from_utf8_lossy(trun)
1483 )));
1484 }
1485 };
1486 match u64::from_str_radix(num.trim(), 8) {
1487 Ok(n) => Ok(n),
1488 Err(_) => Err(other(&format!("numeric field was not a number: {}", num))),
1489 }
1490}
1491
1492fn octal_into<T: fmt::Octal>(dst: &mut [u8], val: T) {
1493 let o = format!("{:o}", val);
1494 let value = once(b'\0').chain(o.bytes().rev().chain(repeat(b'0')));
1495 for (slot, value) in dst.iter_mut().rev().zip(value) {
1496 *slot = value;
1497 }
1498}
1499
1500fn num_field_wrapper_into(dst: &mut [u8], src: u64) {
1503 if src >= 8589934592 || (src >= 2097152 && dst.len() == 8) {
1504 numeric_extended_into(dst, src);
1505 } else {
1506 octal_into(dst, src);
1507 }
1508}
1509
1510fn num_field_wrapper_from(src: &[u8]) -> io::Result<u64> {
1513 if src[0] & 0x80 != 0 {
1514 Ok(numeric_extended_from(src))
1515 } else {
1516 octal_from(src)
1517 }
1518}
1519
1520fn numeric_extended_into(dst: &mut [u8], src: u64) {
1525 let len: usize = dst.len();
1526 for (slot, val) in dst.iter_mut().zip(
1527 repeat(0)
1528 .take(len - 8) .chain((0..8).rev().map(|x| ((src >> (8 * x)) & 0xff) as u8)),
1530 ) {
1531 *slot = val;
1532 }
1533 dst[0] |= 0x80;
1534}
1535
1536fn numeric_extended_from(src: &[u8]) -> u64 {
1537 let mut dst: u64 = 0;
1538 let mut b_to_skip = 1;
1539 if src.len() == 8 {
1540 dst = (src[0] ^ 0x80) as u64;
1542 } else {
1543 b_to_skip = src.len() - 8;
1545 }
1546 for byte in src.iter().skip(b_to_skip) {
1547 dst <<= 8;
1548 dst |= *byte as u64;
1549 }
1550 dst
1551}
1552
1553fn truncate(slice: &[u8]) -> &[u8] {
1554 match slice.iter().position(|i| *i == 0) {
1555 Some(i) => &slice[..i],
1556 None => slice,
1557 }
1558}
1559
1560fn copy_into(slot: &mut [u8], bytes: &[u8]) -> io::Result<()> {
1563 if bytes.len() > slot.len() {
1564 Err(other("provided value is too long"))
1565 } else if bytes.contains(&0) {
1566 Err(other("provided value contains a nul byte"))
1567 } else {
1568 for (slot, val) in slot.iter_mut().zip(bytes.iter().chain(Some(&0))) {
1569 *slot = *val;
1570 }
1571 Ok(())
1572 }
1573}
1574
1575fn copy_path_into_inner(
1576 mut slot: &mut [u8],
1577 path: &Path,
1578 is_link_name: bool,
1579 is_truncated_gnu_long_path: bool,
1580 allow_absolute: bool,
1581) -> io::Result<()> {
1582 let mut emitted = false;
1583 let mut needs_slash = false;
1584 let mut iter = path.components().peekable();
1585 while let Some(component) = iter.next() {
1586 let bytes = path2bytes(Path::new(component.as_os_str()))?;
1587 match (component, is_link_name, allow_absolute) {
1588 (Component::Prefix(..), false, false) | (Component::RootDir, false, false) => {
1589 return Err(other("paths in archives must be relative"));
1590 }
1591 (Component::ParentDir, false, _) => {
1592 if !is_truncated_gnu_long_path || iter.peek().is_some() {
1596 return Err(other("paths in archives must not have `..`"));
1597 }
1598 }
1599 (Component::CurDir, false, _) if path.components().count() == 1 => {}
1601 (Component::CurDir, false, _) => continue,
1602 (Component::Normal(_), _, _)
1603 | (_, true, _)
1604 | (Component::Prefix(_), false, true)
1605 | (Component::RootDir, false, true) => {}
1606 };
1607 if needs_slash {
1608 copy(&mut slot, b"/")?;
1609 }
1610 if bytes.contains(&b'/') {
1611 if let Component::Normal(..) = component {
1612 return Err(other("path component in archive cannot contain `/`"));
1613 }
1614 }
1615 copy(&mut slot, &bytes)?;
1616 if &*bytes != b"/" {
1617 needs_slash = true;
1618 }
1619 emitted = true;
1620 }
1621 if !emitted {
1622 return Err(other("paths in archives must have at least one component"));
1623 }
1624 if ends_with_slash(path) {
1625 copy(&mut slot, b"/")?;
1626 }
1627 return Ok(());
1628
1629 fn copy(slot: &mut &mut [u8], bytes: &[u8]) -> io::Result<()> {
1630 copy_into(slot, bytes)?;
1631 let tmp = mem::take(slot);
1632 *slot = &mut tmp[bytes.len()..];
1633 Ok(())
1634 }
1635}
1636
1637fn copy_path_into(
1646 slot: &mut [u8],
1647 path: &Path,
1648 is_link_name: bool,
1649 allow_absolute: bool,
1650) -> io::Result<()> {
1651 copy_path_into_inner(slot, path, is_link_name, false, allow_absolute)
1652}
1653
1654fn copy_path_into_gnu_long(
1665 slot: &mut [u8],
1666 path: &Path,
1667 is_link_name: bool,
1668 allow_absolute: bool,
1669) -> io::Result<()> {
1670 copy_path_into_inner(slot, path, is_link_name, true, allow_absolute)
1671}
1672
1673#[cfg(target_arch = "wasm32")]
1674fn ends_with_slash(p: &Path) -> bool {
1675 p.to_string_lossy().ends_with('/')
1676}
1677
1678#[cfg(windows)]
1679fn ends_with_slash(p: &Path) -> bool {
1680 let last = p.as_os_str().encode_wide().last();
1681 last == Some(b'/' as u16) || last == Some(b'\\' as u16)
1682}
1683
1684#[cfg(all(unix, not(target_arch = "wasm32")))]
1685fn ends_with_slash(p: &Path) -> bool {
1686 p.as_os_str().as_bytes().ends_with(b"/")
1687}
1688
1689#[cfg(any(windows, target_arch = "wasm32"))]
1690pub fn path2bytes(p: &Path) -> io::Result<Cow<'_, [u8]>> {
1691 p.as_os_str()
1692 .to_str()
1693 .map(|s| s.as_bytes())
1694 .ok_or_else(|| other(&format!("path {} was not valid Unicode", p.display())))
1695 .map(|bytes| {
1696 if bytes.contains(&b'\\') {
1697 let mut bytes = bytes.to_owned();
1699 for b in &mut bytes {
1700 if *b == b'\\' {
1701 *b = b'/';
1702 }
1703 }
1704 Cow::Owned(bytes)
1705 } else {
1706 Cow::Borrowed(bytes)
1707 }
1708 })
1709}
1710
1711#[cfg(all(unix, not(target_arch = "wasm32")))]
1712pub fn path2bytes(p: &Path) -> io::Result<Cow<'_, [u8]>> {
1714 Ok(Cow::Borrowed(p.as_os_str().as_bytes()))
1715}
1716
1717#[cfg(windows)]
1718pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1721 return match bytes {
1722 Cow::Borrowed(bytes) => {
1723 let s = str::from_utf8(bytes).map_err(|_| not_unicode(bytes))?;
1724 Ok(Cow::Borrowed(Path::new(s)))
1725 }
1726 Cow::Owned(bytes) => {
1727 let s = String::from_utf8(bytes).map_err(|uerr| not_unicode(&uerr.into_bytes()))?;
1728 Ok(Cow::Owned(PathBuf::from(s)))
1729 }
1730 };
1731
1732 fn not_unicode(v: &[u8]) -> io::Error {
1733 other(&format!(
1734 "only Unicode paths are supported on Windows: {}",
1735 String::from_utf8_lossy(v)
1736 ))
1737 }
1738}
1739
1740#[cfg(all(unix, not(target_arch = "wasm32")))]
1741pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1743 use std::ffi::{OsStr, OsString};
1744
1745 Ok(match bytes {
1746 Cow::Borrowed(bytes) => Cow::Borrowed(Path::new(OsStr::from_bytes(bytes))),
1747 Cow::Owned(bytes) => Cow::Owned(PathBuf::from(OsString::from_vec(bytes))),
1748 })
1749}
1750
1751#[cfg(target_arch = "wasm32")]
1752pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1753 Ok(match bytes {
1754 Cow::Borrowed(bytes) => {
1755 Cow::Borrowed(Path::new(str::from_utf8(bytes).map_err(invalid_utf8)?))
1756 }
1757 Cow::Owned(bytes) => Cow::Owned(PathBuf::from(
1758 String::from_utf8(bytes).map_err(invalid_utf8)?,
1759 )),
1760 })
1761}
1762
1763#[cfg(target_arch = "wasm32")]
1764fn invalid_utf8<T>(_: T) -> io::Error {
1765 io::Error::new(io::ErrorKind::InvalidData, "Invalid utf-8")
1766}