1use crate::util::bounded::{LengthExceeded, str::BoundedString};
4use crate::{Duration, UnknownValueError};
5use crate::{RawChunk, entry::ExtendedAttribute};
6use std::io::{self, Read};
7use std::ops::Deref;
8use std::str;
9
10#[allow(deprecated)]
26#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
27pub struct Metadata {
28 pub(crate) raw_file_size: Option<u128>,
29 pub(crate) compressed_size: usize,
30 pub(crate) created: Option<Duration>,
31 pub(crate) modified: Option<Duration>,
32 pub(crate) accessed: Option<Duration>,
33 pub(crate) permission: Option<Permission>,
34 pub(crate) link_target_type: Option<LinkTargetType>,
35 pub(crate) owner_uid: Option<OwnerUid>,
36 pub(crate) owner_gid: Option<OwnerGid>,
37 pub(crate) owner_user_name: Option<OwnerUserName>,
38 pub(crate) owner_group_name: Option<OwnerGroupName>,
39 pub(crate) owner_user_sid: Option<OwnerUserSid>,
40 pub(crate) owner_group_sid: Option<OwnerGroupSid>,
41 pub(crate) permission_mode: Option<PermissionMode>,
42 pub(crate) xattrs: Vec<ExtendedAttribute>,
43}
44
45pub struct EntryWriteAttributes {
56 pub(crate) metadata: Metadata,
57 pub(crate) extra_chunks: Vec<RawChunk>,
58}
59
60impl EntryWriteAttributes {
61 #[inline]
63 pub fn new(metadata: Metadata) -> Self {
64 Self {
65 metadata,
66 extra_chunks: Vec::new(),
67 }
68 }
69
70 #[inline]
72 pub const fn metadata(&self) -> &Metadata {
73 &self.metadata
74 }
75
76 #[inline]
78 pub fn add_extra_chunk<T>(&mut self, chunk: T) -> &mut Self
79 where
80 T: Into<RawChunk>,
81 {
82 self.extra_chunks.push(chunk.into());
83 self
84 }
85}
86
87impl From<Metadata> for EntryWriteAttributes {
88 #[inline]
89 fn from(metadata: Metadata) -> Self {
90 Self::new(metadata)
91 }
92}
93
94impl Metadata {
95 #[inline]
97 pub const fn new() -> Self {
98 Self {
99 raw_file_size: Some(0),
100 compressed_size: 0,
101 created: None,
102 modified: None,
103 accessed: None,
104 permission: None,
105 link_target_type: None,
106 owner_uid: None,
107 owner_gid: None,
108 owner_user_name: None,
109 owner_group_name: None,
110 owner_user_sid: None,
111 owner_group_sid: None,
112 permission_mode: None,
113 xattrs: Vec::new(),
114 }
115 }
116
117 #[inline]
131 pub const fn with_created(mut self, created: Option<Duration>) -> Self {
132 self.created = created;
133 self
134 }
135
136 #[inline]
150 pub const fn with_modified(mut self, modified: Option<Duration>) -> Self {
151 self.modified = modified;
152 self
153 }
154
155 #[inline]
169 pub const fn with_accessed(mut self, accessed: Option<Duration>) -> Self {
170 self.accessed = accessed;
171 self
172 }
173
174 #[deprecated(
176 since = "0.34.0",
177 note = "the fPRM chunk is superseded by the owner facet chunks; use Metadata::with_owner_uid/with_owner_gid/with_owner_user_name/with_owner_group_name/with_owner_user_sid/with_owner_group_sid/with_permission_mode"
178 )]
179 #[allow(deprecated)]
180 #[inline]
181 pub fn with_permission(mut self, permission: Option<Permission>) -> Self {
182 self.permission = permission;
183 self
184 }
185
186 #[inline]
188 pub fn with_owner_uid(mut self, value: Option<OwnerUid>) -> Self {
189 self.owner_uid = value;
190 self
191 }
192 #[inline]
194 pub fn with_owner_gid(mut self, value: Option<OwnerGid>) -> Self {
195 self.owner_gid = value;
196 self
197 }
198 #[inline]
200 pub fn with_owner_user_name(mut self, value: Option<OwnerUserName>) -> Self {
201 self.owner_user_name = value;
202 self
203 }
204 #[inline]
206 pub fn with_owner_group_name(mut self, value: Option<OwnerGroupName>) -> Self {
207 self.owner_group_name = value;
208 self
209 }
210 #[inline]
212 pub fn with_owner_user_sid(mut self, value: Option<OwnerUserSid>) -> Self {
213 self.owner_user_sid = value;
214 self
215 }
216 #[inline]
218 pub fn with_owner_group_sid(mut self, value: Option<OwnerGroupSid>) -> Self {
219 self.owner_group_sid = value;
220 self
221 }
222 #[inline]
224 pub fn with_permission_mode(mut self, value: Option<PermissionMode>) -> Self {
225 self.permission_mode = value;
226 self
227 }
228
229 #[inline]
232 pub const fn with_link_target_type(mut self, link_target_type: Option<LinkTargetType>) -> Self {
233 self.link_target_type = link_target_type;
234 self
235 }
236
237 #[inline]
242 pub fn with_xattrs(mut self, xattrs: impl Into<Vec<ExtendedAttribute>>) -> Self {
243 self.xattrs = xattrs.into();
244 self
245 }
246
247 #[inline]
249 pub const fn raw_file_size(&self) -> Option<u128> {
250 self.raw_file_size
251 }
252 #[inline]
254 pub const fn compressed_size(&self) -> usize {
255 self.compressed_size
256 }
257 #[inline]
259 pub const fn created(&self) -> Option<Duration> {
260 self.created
261 }
262 #[inline]
264 pub const fn modified(&self) -> Option<Duration> {
265 self.modified
266 }
267 #[inline]
269 pub const fn accessed(&self) -> Option<Duration> {
270 self.accessed
271 }
272 #[deprecated(
274 since = "0.34.0",
275 note = "the fPRM chunk is superseded by the owner facet chunks; use Metadata::owner_uid/owner_gid/owner_user_name/owner_group_name/owner_user_sid/owner_group_sid/permission_mode"
276 )]
277 #[allow(deprecated)]
278 #[inline]
279 pub const fn permission(&self) -> Option<&Permission> {
280 self.permission.as_ref()
281 }
282 #[inline]
284 pub const fn owner_uid(&self) -> Option<OwnerUid> {
285 self.owner_uid
286 }
287 #[inline]
289 pub const fn owner_gid(&self) -> Option<OwnerGid> {
290 self.owner_gid
291 }
292 #[inline]
294 pub fn owner_user_name(&self) -> Option<&OwnerUserName> {
295 self.owner_user_name.as_ref()
296 }
297 #[inline]
299 pub fn owner_group_name(&self) -> Option<&OwnerGroupName> {
300 self.owner_group_name.as_ref()
301 }
302 #[inline]
304 pub fn owner_user_sid(&self) -> Option<&OwnerUserSid> {
305 self.owner_user_sid.as_ref()
306 }
307 #[inline]
309 pub fn owner_group_sid(&self) -> Option<&OwnerGroupSid> {
310 self.owner_group_sid.as_ref()
311 }
312 #[inline]
314 pub const fn permission_mode(&self) -> Option<PermissionMode> {
315 self.permission_mode
316 }
317
318 #[inline]
324 pub const fn link_target_type(&self) -> Option<LinkTargetType> {
325 self.link_target_type
326 }
327
328 #[inline]
330 pub fn xattrs(&self) -> &[ExtendedAttribute] {
331 &self.xattrs
332 }
333}
334
335impl Default for Metadata {
336 #[inline]
337 fn default() -> Self {
338 Self::new()
339 }
340}
341
342#[deprecated(
344 since = "0.34.0",
345 note = "the fPRM chunk is superseded by the owner facet chunks; use the owner facet API (Metadata::owner_uid/owner_gid/owner_user_name/owner_group_name/owner_user_sid/owner_group_sid/permission_mode and the matching Metadata::with_* setters)"
346)]
347#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
348pub struct Permission {
349 uid: u64,
350 uname: String,
351 gid: u64,
352 gname: String,
353 permission: u16,
354}
355
356#[allow(deprecated)]
357impl Permission {
358 #[deprecated(
372 since = "0.34.0",
373 note = "the fPRM chunk is superseded by the owner facet chunks; use the owner facet API (Metadata::owner_uid/owner_gid/owner_user_name/owner_group_name/owner_user_sid/owner_group_sid/permission_mode and the matching Metadata::with_* setters)"
374 )]
375 #[inline]
376 pub const fn new(uid: u64, uname: String, gid: u64, gname: String, permission: u16) -> Self {
377 Self {
378 uid,
379 uname,
380 gid,
381 gname,
382 permission,
383 }
384 }
385 #[deprecated(
397 since = "0.34.0",
398 note = "the fPRM chunk is superseded by the owner facet chunks; use the owner facet API (Metadata::owner_uid/owner_gid/owner_user_name/owner_group_name/owner_user_sid/owner_group_sid/permission_mode and the matching Metadata::with_* setters)"
399 )]
400 #[inline]
401 pub const fn uid(&self) -> u64 {
402 self.uid
403 }
404
405 #[deprecated(
417 since = "0.34.0",
418 note = "the fPRM chunk is superseded by the owner facet chunks; use the owner facet API (Metadata::owner_uid/owner_gid/owner_user_name/owner_group_name/owner_user_sid/owner_group_sid/permission_mode and the matching Metadata::with_* setters)"
419 )]
420 #[inline]
421 pub fn uname(&self) -> &str {
422 &self.uname
423 }
424
425 #[deprecated(
437 since = "0.34.0",
438 note = "the fPRM chunk is superseded by the owner facet chunks; use the owner facet API (Metadata::owner_uid/owner_gid/owner_user_name/owner_group_name/owner_user_sid/owner_group_sid/permission_mode and the matching Metadata::with_* setters)"
439 )]
440 #[inline]
441 pub const fn gid(&self) -> u64 {
442 self.gid
443 }
444
445 #[deprecated(
457 since = "0.34.0",
458 note = "the fPRM chunk is superseded by the owner facet chunks; use the owner facet API (Metadata::owner_uid/owner_gid/owner_user_name/owner_group_name/owner_user_sid/owner_group_sid/permission_mode and the matching Metadata::with_* setters)"
459 )]
460 #[inline]
461 pub fn gname(&self) -> &str {
462 &self.gname
463 }
464
465 #[deprecated(
477 since = "0.34.0",
478 note = "the fPRM chunk is superseded by the owner facet chunks; use the owner facet API (Metadata::owner_uid/owner_gid/owner_user_name/owner_group_name/owner_user_sid/owner_group_sid/permission_mode and the matching Metadata::with_* setters)"
479 )]
480 #[inline]
481 pub const fn permissions(&self) -> u16 {
482 self.permission
483 }
484
485 pub(crate) fn to_bytes(&self) -> Vec<u8> {
486 let mut bytes = Vec::with_capacity(20 + self.uname.len() + self.gname.len());
487 bytes.extend_from_slice(&self.uid.to_be_bytes());
488 bytes.extend_from_slice(&(self.uname.len() as u8).to_be_bytes());
489 bytes.extend_from_slice(self.uname.as_bytes());
490 bytes.extend_from_slice(&self.gid.to_be_bytes());
491 bytes.extend_from_slice(&(self.gname.len() as u8).to_be_bytes());
492 bytes.extend_from_slice(self.gname.as_bytes());
493 bytes.extend_from_slice(&self.permission.to_be_bytes());
494 bytes
495 }
496
497 pub(crate) fn try_from_bytes(mut bytes: &[u8]) -> io::Result<Self> {
498 let uid = u64::from_be_bytes({
499 let mut buf = [0; 8];
500 bytes.read_exact(&mut buf)?;
501 buf
502 });
503 let uname_len = {
504 let mut buf = [0; 1];
505 bytes.read_exact(&mut buf)?;
506 buf[0] as usize
507 };
508 let uname = String::from_utf8({
509 let mut buf = vec![0; uname_len];
510 bytes.read_exact(&mut buf)?;
511 buf
512 })
513 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
514 let gid = u64::from_be_bytes({
515 let mut buf = [0; 8];
516 bytes.read_exact(&mut buf)?;
517 buf
518 });
519 let gname_len = {
520 let mut buf = [0; 1];
521 bytes.read_exact(&mut buf)?;
522 buf[0] as usize
523 };
524 let gname = String::from_utf8({
525 let mut buf = vec![0; gname_len];
526 bytes.read_exact(&mut buf)?;
527 buf
528 })
529 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
530 let permission = u16::from_be_bytes({
531 let mut buf = [0; 2];
532 bytes.read_exact(&mut buf)?;
533 buf
534 });
535 Ok(Self {
536 uid,
537 uname,
538 gid,
539 gname,
540 permission,
541 })
542 }
543}
544
545const OWNER_STR_MAX: usize = u8::MAX as usize;
548
549#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
551#[repr(transparent)]
552pub struct OwnerUserName(BoundedString<OWNER_STR_MAX>);
553
554impl OwnerUserName {
555 #[inline]
561 pub fn new(value: impl Into<Box<str>>) -> Result<Self, LengthExceeded> {
562 BoundedString::new(value).map(Self)
563 }
564 #[inline]
566 #[must_use]
567 pub fn as_str(&self) -> &str {
568 self.0.as_str()
569 }
570 pub(crate) fn to_bytes(&self) -> Vec<u8> {
571 let b = self.0.as_str().as_bytes();
572 let mut v = Vec::with_capacity(1 + b.len());
573 v.push(b.len() as u8);
575 v.extend_from_slice(b);
576 v
577 }
578 pub(crate) fn try_from_bytes(bytes: &[u8]) -> io::Result<Self> {
579 let (&len, rest) = bytes.split_first().ok_or(io::ErrorKind::UnexpectedEof)?;
580 let s = rest
581 .get(..len as usize)
582 .ok_or(io::ErrorKind::UnexpectedEof)?;
583 let s = str::from_utf8(s).map_err(|_| io::ErrorKind::InvalidData)?;
584 Self::new(s.to_owned()).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
585 }
586}
587
588impl Deref for OwnerUserName {
589 type Target = str;
590 #[inline]
591 fn deref(&self) -> &str {
592 self.0.as_str()
593 }
594}
595impl TryFrom<String> for OwnerUserName {
596 type Error = LengthExceeded;
597 #[inline]
598 fn try_from(value: String) -> Result<Self, Self::Error> {
599 Self::new(value)
600 }
601}
602impl TryFrom<&str> for OwnerUserName {
603 type Error = LengthExceeded;
604 #[inline]
605 fn try_from(value: &str) -> Result<Self, Self::Error> {
606 Self::new(value)
607 }
608}
609impl From<OwnerUserName> for String {
610 #[inline]
611 fn from(value: OwnerUserName) -> Self {
612 value.0.into()
613 }
614}
615
616#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
618#[repr(transparent)]
619pub struct OwnerGroupName(BoundedString<OWNER_STR_MAX>);
620
621impl OwnerGroupName {
622 #[inline]
628 pub fn new(value: impl Into<Box<str>>) -> Result<Self, LengthExceeded> {
629 BoundedString::new(value).map(Self)
630 }
631 #[inline]
633 #[must_use]
634 pub fn as_str(&self) -> &str {
635 self.0.as_str()
636 }
637 pub(crate) fn to_bytes(&self) -> Vec<u8> {
638 let b = self.0.as_str().as_bytes();
639 let mut v = Vec::with_capacity(1 + b.len());
640 v.push(b.len() as u8);
642 v.extend_from_slice(b);
643 v
644 }
645 pub(crate) fn try_from_bytes(bytes: &[u8]) -> io::Result<Self> {
646 let (&len, rest) = bytes.split_first().ok_or(io::ErrorKind::UnexpectedEof)?;
647 let s = rest
648 .get(..len as usize)
649 .ok_or(io::ErrorKind::UnexpectedEof)?;
650 let s = str::from_utf8(s).map_err(|_| io::ErrorKind::InvalidData)?;
651 Self::new(s.to_owned()).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
652 }
653}
654
655impl Deref for OwnerGroupName {
656 type Target = str;
657 #[inline]
658 fn deref(&self) -> &str {
659 self.0.as_str()
660 }
661}
662impl TryFrom<String> for OwnerGroupName {
663 type Error = LengthExceeded;
664 #[inline]
665 fn try_from(value: String) -> Result<Self, Self::Error> {
666 Self::new(value)
667 }
668}
669impl TryFrom<&str> for OwnerGroupName {
670 type Error = LengthExceeded;
671 #[inline]
672 fn try_from(value: &str) -> Result<Self, Self::Error> {
673 Self::new(value)
674 }
675}
676impl From<OwnerGroupName> for String {
677 #[inline]
678 fn from(value: OwnerGroupName) -> Self {
679 value.0.into()
680 }
681}
682
683#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
685#[repr(transparent)]
686pub struct OwnerUserSid(BoundedString<OWNER_STR_MAX>);
687
688impl OwnerUserSid {
689 #[inline]
695 pub fn new(value: impl Into<Box<str>>) -> Result<Self, LengthExceeded> {
696 BoundedString::new(value).map(Self)
697 }
698 #[inline]
700 #[must_use]
701 pub fn as_str(&self) -> &str {
702 self.0.as_str()
703 }
704 pub(crate) fn to_bytes(&self) -> Vec<u8> {
705 let b = self.0.as_str().as_bytes();
706 let mut v = Vec::with_capacity(1 + b.len());
707 v.push(b.len() as u8);
709 v.extend_from_slice(b);
710 v
711 }
712 pub(crate) fn try_from_bytes(bytes: &[u8]) -> io::Result<Self> {
713 let (&len, rest) = bytes.split_first().ok_or(io::ErrorKind::UnexpectedEof)?;
714 let s = rest
715 .get(..len as usize)
716 .ok_or(io::ErrorKind::UnexpectedEof)?;
717 let s = str::from_utf8(s).map_err(|_| io::ErrorKind::InvalidData)?;
718 Self::new(s.to_owned()).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
719 }
720}
721
722impl Deref for OwnerUserSid {
723 type Target = str;
724 #[inline]
725 fn deref(&self) -> &str {
726 self.0.as_str()
727 }
728}
729impl TryFrom<String> for OwnerUserSid {
730 type Error = LengthExceeded;
731 #[inline]
732 fn try_from(value: String) -> Result<Self, Self::Error> {
733 Self::new(value)
734 }
735}
736impl TryFrom<&str> for OwnerUserSid {
737 type Error = LengthExceeded;
738 #[inline]
739 fn try_from(value: &str) -> Result<Self, Self::Error> {
740 Self::new(value)
741 }
742}
743impl From<OwnerUserSid> for String {
744 #[inline]
745 fn from(value: OwnerUserSid) -> Self {
746 value.0.into()
747 }
748}
749
750#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
752#[repr(transparent)]
753pub struct OwnerGroupSid(BoundedString<OWNER_STR_MAX>);
754
755impl OwnerGroupSid {
756 #[inline]
762 pub fn new(value: impl Into<Box<str>>) -> Result<Self, LengthExceeded> {
763 BoundedString::new(value).map(Self)
764 }
765 #[inline]
767 #[must_use]
768 pub fn as_str(&self) -> &str {
769 self.0.as_str()
770 }
771 pub(crate) fn to_bytes(&self) -> Vec<u8> {
772 let b = self.0.as_str().as_bytes();
773 let mut v = Vec::with_capacity(1 + b.len());
774 v.push(b.len() as u8);
776 v.extend_from_slice(b);
777 v
778 }
779 pub(crate) fn try_from_bytes(bytes: &[u8]) -> io::Result<Self> {
780 let (&len, rest) = bytes.split_first().ok_or(io::ErrorKind::UnexpectedEof)?;
781 let s = rest
782 .get(..len as usize)
783 .ok_or(io::ErrorKind::UnexpectedEof)?;
784 let s = str::from_utf8(s).map_err(|_| io::ErrorKind::InvalidData)?;
785 Self::new(s.to_owned()).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
786 }
787}
788
789impl Deref for OwnerGroupSid {
790 type Target = str;
791 #[inline]
792 fn deref(&self) -> &str {
793 self.0.as_str()
794 }
795}
796impl TryFrom<String> for OwnerGroupSid {
797 type Error = LengthExceeded;
798 #[inline]
799 fn try_from(value: String) -> Result<Self, Self::Error> {
800 Self::new(value)
801 }
802}
803impl TryFrom<&str> for OwnerGroupSid {
804 type Error = LengthExceeded;
805 #[inline]
806 fn try_from(value: &str) -> Result<Self, Self::Error> {
807 Self::new(value)
808 }
809}
810impl From<OwnerGroupSid> for String {
811 #[inline]
812 fn from(value: OwnerGroupSid) -> Self {
813 value.0.into()
814 }
815}
816
817#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
819#[repr(transparent)]
820pub struct OwnerUid(u64);
821
822impl OwnerUid {
823 #[inline]
825 #[must_use]
826 pub const fn get(self) -> u64 {
827 self.0
828 }
829 pub(crate) fn to_bytes(self) -> [u8; 8] {
830 self.0.to_be_bytes()
831 }
832 pub(crate) fn try_from_bytes(bytes: &[u8]) -> io::Result<Self> {
833 let a: [u8; 8] = bytes
834 .try_into()
835 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "fUId must be 8 bytes"))?;
836 Ok(Self(u64::from_be_bytes(a)))
837 }
838}
839impl From<u64> for OwnerUid {
840 #[inline]
841 fn from(v: u64) -> Self {
842 Self(v)
843 }
844}
845
846#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
848#[repr(transparent)]
849pub struct OwnerGid(u64);
850
851impl OwnerGid {
852 #[inline]
854 #[must_use]
855 pub const fn get(self) -> u64 {
856 self.0
857 }
858 pub(crate) fn to_bytes(self) -> [u8; 8] {
859 self.0.to_be_bytes()
860 }
861 pub(crate) fn try_from_bytes(bytes: &[u8]) -> io::Result<Self> {
862 let a: [u8; 8] = bytes
863 .try_into()
864 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "fGId must be 8 bytes"))?;
865 Ok(Self(u64::from_be_bytes(a)))
866 }
867}
868impl From<u64> for OwnerGid {
869 #[inline]
870 fn from(v: u64) -> Self {
871 Self(v)
872 }
873}
874
875#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
878#[repr(transparent)]
879pub struct PermissionMode(u16);
880
881impl PermissionMode {
882 #[inline]
884 #[must_use]
885 pub const fn get(self) -> u16 {
886 self.0
887 }
888 pub(crate) fn to_bytes(self) -> [u8; 2] {
889 self.0.to_be_bytes()
890 }
891 pub(crate) fn try_from_bytes(bytes: &[u8]) -> io::Result<Self> {
892 let a: [u8; 2] = bytes
893 .try_into()
894 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "fMOd must be 2 bytes"))?;
895 Ok(Self::from(u16::from_be_bytes(a)))
896 }
897}
898impl From<u16> for PermissionMode {
899 #[inline]
900 fn from(v: u16) -> Self {
901 Self(v & 0o7777)
902 }
903}
904
905#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
929#[repr(u8)]
930pub enum LinkTargetType {
931 Unknown = 0,
933 File = 1,
935 Directory = 2,
937}
938
939impl TryFrom<u8> for LinkTargetType {
940 type Error = UnknownValueError;
941
942 #[inline]
943 fn try_from(value: u8) -> Result<Self, Self::Error> {
944 match value {
945 0 => Ok(Self::Unknown),
946 1 => Ok(Self::File),
947 2 => Ok(Self::Directory),
948 value => Err(UnknownValueError(value)),
949 }
950 }
951}
952
953impl LinkTargetType {
954 pub(crate) fn to_bytes(self) -> [u8; 1] {
955 [self as u8]
956 }
957
958 pub(crate) fn try_from_bytes(mut bytes: &[u8]) -> io::Result<Option<Self>> {
964 let mut buf = [0u8; 1];
965 bytes.read_exact(&mut buf)?;
966 Ok(Self::try_from(buf[0]).ok())
967 }
968}
969
970#[cfg(test)]
971mod tests {
972 use super::*;
973 #[cfg(all(target_family = "wasm", target_os = "unknown"))]
974 use wasm_bindgen_test::wasm_bindgen_test as test;
975
976 #[allow(deprecated)]
977 #[test]
978 fn permission() {
979 let perm = Permission::new(1000, "user1".into(), 100, "group1".into(), 0o644);
980 assert_eq!(perm, Permission::try_from_bytes(&perm.to_bytes()).unwrap());
981 }
982
983 #[test]
984 fn owner_string_newtype_bound_and_codec() {
985 use crate::entry::OwnerUserName;
986 assert!(OwnerUserName::new("").is_ok());
987 assert!(OwnerUserName::new("alice").is_ok());
988 assert!(OwnerUserName::new("a".repeat(255)).is_ok());
989 assert!(OwnerUserName::new("a".repeat(256)).is_err());
990 let n = OwnerUserName::new("alice").unwrap();
991 assert_eq!(n.to_bytes(), vec![5, b'a', b'l', b'i', b'c', b'e']);
992 assert_eq!(OwnerUserName::try_from_bytes(&n.to_bytes()).unwrap(), n);
993 assert_eq!(OwnerUserName::try_from_bytes(&[0]).unwrap().as_str(), "");
994 assert_eq!(
995 OwnerUserName::try_from_bytes(&[3, b'a', b'b', b'c', 0xFF])
996 .unwrap()
997 .as_str(),
998 "abc"
999 );
1000 assert!(OwnerUserName::try_from_bytes(&[]).is_err());
1001 assert!(OwnerUserName::try_from_bytes(&[5, b'a']).is_err());
1002 assert!(OwnerUserName::try_from_bytes(&[1, 0xFF]).is_err());
1003 }
1004
1005 #[test]
1006 fn owner_uid_and_permission_mode_codec() {
1007 use crate::entry::{OwnerUid, PermissionMode};
1008 let u = OwnerUid::from(1000u64);
1009 assert_eq!(u.get(), 1000);
1010 assert_eq!(u.to_bytes(), 1000u64.to_be_bytes());
1011 assert_eq!(OwnerUid::try_from_bytes(&u.to_bytes()).unwrap(), u);
1012 assert!(OwnerUid::try_from_bytes(&[0, 0, 0]).is_err());
1013 assert_eq!(PermissionMode::from(0o7777u16).get(), 0o7777);
1014 assert_eq!(PermissionMode::from(0o170755u16).get(), 0o0755);
1015 let m = PermissionMode::from(0o644u16);
1016 assert_eq!(m.to_bytes(), 0o644u16.to_be_bytes());
1017 assert_eq!(PermissionMode::try_from_bytes(&m.to_bytes()).unwrap(), m);
1018 assert_eq!(
1019 PermissionMode::try_from_bytes(&0o170644u16.to_be_bytes())
1020 .unwrap()
1021 .get(),
1022 0o0644
1023 );
1024 assert!(PermissionMode::try_from_bytes(&[0]).is_err());
1025 }
1026
1027 #[test]
1028 fn metadata_owner_facets_default_none() {
1029 let m = Metadata::new();
1030 assert_eq!(m.owner_uid(), None);
1031 assert_eq!(m.owner_gid(), None);
1032 assert_eq!(m.owner_user_name(), None);
1033 assert_eq!(m.owner_group_name(), None);
1034 assert_eq!(m.owner_user_sid(), None);
1035 assert_eq!(m.owner_group_sid(), None);
1036 assert_eq!(m.permission_mode(), None);
1037 }
1038
1039 #[test]
1040 fn owner_id_facets_round_trip_via_entry() {
1041 use crate::entry::{OwnerGid, OwnerUid};
1042 use crate::{Archive, FileEntryBuilder};
1043 let mut buf = Vec::new();
1044 {
1045 let mut archive = Archive::write_header(&mut buf).unwrap();
1046 let mut b = FileEntryBuilder::new("f".into()).unwrap();
1047 b.metadata(
1048 Metadata::new()
1049 .with_owner_uid(Some(OwnerUid::from(1000)))
1050 .with_owner_gid(Some(OwnerGid::from(2000))),
1051 );
1052 let entry = b.build().unwrap();
1053 archive.add_entry(entry).unwrap();
1054 archive.finalize().unwrap();
1055 }
1056 let mut archive = Archive::read_header(&buf[..]).unwrap();
1057 let entry = archive.entries().skip_solid().next().unwrap().unwrap();
1058 let m = entry.metadata();
1059 assert_eq!(m.owner_uid().map(|v| v.get()), Some(1000));
1060 assert_eq!(m.owner_gid().map(|v| v.get()), Some(2000));
1061 }
1062
1063 #[test]
1064 fn owner_name_facets_round_trip_via_entry() {
1065 use crate::entry::{OwnerGroupName, OwnerUserName};
1066 use crate::{Archive, FileEntryBuilder};
1067 let mut buf = Vec::new();
1068 {
1069 let mut archive = Archive::write_header(&mut buf).unwrap();
1070 let mut b = FileEntryBuilder::new("f".into()).unwrap();
1071 b.metadata(
1072 Metadata::new()
1073 .with_owner_user_name(Some(OwnerUserName::new("alice").unwrap()))
1074 .with_owner_group_name(Some(OwnerGroupName::new("").unwrap())),
1075 );
1076 let entry = b.build().unwrap();
1077 archive.add_entry(entry).unwrap();
1078 archive.finalize().unwrap();
1079 }
1080 let mut archive = Archive::read_header(&buf[..]).unwrap();
1081 let entry = archive.entries().skip_solid().next().unwrap().unwrap();
1082 let m = entry.metadata();
1083 assert_eq!(m.owner_user_name().map(|v| v.as_str()), Some("alice"));
1084 assert_eq!(m.owner_group_name().map(|v| v.as_str()), Some("")); }
1086
1087 #[test]
1088 fn owner_sid_facets_round_trip_via_entry() {
1089 use crate::entry::{OwnerGroupSid, OwnerUserSid};
1090 use crate::{Archive, FileEntryBuilder};
1091 let mut buf = Vec::new();
1092 {
1093 let mut archive = Archive::write_header(&mut buf).unwrap();
1094 let mut b = FileEntryBuilder::new("f".into()).unwrap();
1095 b.metadata(
1096 Metadata::new()
1097 .with_owner_user_sid(Some(OwnerUserSid::new("S-1-5-21-1-2-3-1001").unwrap()))
1098 .with_owner_group_sid(Some(OwnerGroupSid::new("S-1-5-32-544").unwrap())),
1099 );
1100 let entry = b.build().unwrap();
1101 archive.add_entry(entry).unwrap();
1102 archive.finalize().unwrap();
1103 }
1104 let mut archive = Archive::read_header(&buf[..]).unwrap();
1105 let entry = archive.entries().skip_solid().next().unwrap().unwrap();
1106 let m = entry.metadata();
1107 assert_eq!(
1108 m.owner_user_sid().map(|v| v.as_str()),
1109 Some("S-1-5-21-1-2-3-1001")
1110 );
1111 assert_eq!(
1112 m.owner_group_sid().map(|v| v.as_str()),
1113 Some("S-1-5-32-544")
1114 );
1115 }
1116
1117 #[test]
1118 fn fosi_length_prefixed_round_trip_and_empty() {
1119 use crate::entry::{OwnerGroupSid, OwnerUserSid};
1120 use crate::{Archive, FileEntryBuilder};
1121 let mut buf = Vec::new();
1122 {
1123 let mut a = Archive::write_header(&mut buf).unwrap();
1124 let mut b = FileEntryBuilder::new("f".into()).unwrap();
1125 b.metadata(
1126 Metadata::new()
1127 .with_owner_user_sid(Some(OwnerUserSid::new("S-1-5-21-1-2-3-1001").unwrap()))
1128 .with_owner_group_sid(Some(OwnerGroupSid::new("").unwrap())), );
1130 a.add_entry(b.build().unwrap()).unwrap();
1131 a.finalize().unwrap();
1132 }
1133 let mut a = Archive::read_header(&buf[..]).unwrap();
1134 let e = a.entries().skip_solid().next().unwrap().unwrap();
1135 assert_eq!(
1136 e.metadata().owner_user_sid().map(|v| v.as_str()),
1137 Some("S-1-5-21-1-2-3-1001")
1138 );
1139 assert_eq!(e.metadata().owner_group_sid().map(|v| v.as_str()), Some(""));
1140 }
1141
1142 #[test]
1143 fn permission_mode_facet_round_trip_via_entry() {
1144 use crate::entry::PermissionMode;
1145 use crate::{Archive, FileEntryBuilder};
1146 let mut buf = Vec::new();
1147 {
1148 let mut archive = Archive::write_header(&mut buf).unwrap();
1149 let mut b = FileEntryBuilder::new("f".into()).unwrap();
1150 b.metadata(Metadata::new().with_permission_mode(Some(PermissionMode::from(0o750))));
1151 let entry = b.build().unwrap();
1152 archive.add_entry(entry).unwrap();
1153 archive.finalize().unwrap();
1154 }
1155 let mut archive = Archive::read_header(&buf[..]).unwrap();
1156 let entry = archive.entries().skip_solid().next().unwrap().unwrap();
1157 assert_eq!(
1158 entry.metadata().permission_mode().map(|v| v.get()),
1159 Some(0o750)
1160 );
1161 }
1162
1163 #[test]
1164 fn link_target_type_roundtrip_unknown() {
1165 let ltp = LinkTargetType::Unknown;
1166 assert_eq!(
1167 Some(ltp),
1168 LinkTargetType::try_from_bytes(<p.to_bytes()).unwrap()
1169 );
1170 }
1171
1172 #[test]
1173 fn link_target_type_roundtrip_file() {
1174 let ltp = LinkTargetType::File;
1175 assert_eq!(
1176 Some(ltp),
1177 LinkTargetType::try_from_bytes(<p.to_bytes()).unwrap()
1178 );
1179 }
1180
1181 #[test]
1182 fn link_target_type_roundtrip_directory() {
1183 let ltp = LinkTargetType::Directory;
1184 assert_eq!(
1185 Some(ltp),
1186 LinkTargetType::try_from_bytes(<p.to_bytes()).unwrap()
1187 );
1188 }
1189
1190 #[test]
1191 fn link_target_type_unknown_values_return_none() {
1192 assert_eq!(LinkTargetType::try_from_bytes(&[0x03]).unwrap(), None);
1193 assert_eq!(LinkTargetType::try_from_bytes(&[0xFF]).unwrap(), None);
1194 }
1195
1196 #[test]
1197 fn link_target_type_empty_bytes() {
1198 assert!(LinkTargetType::try_from_bytes(&[]).is_err());
1199 }
1200
1201 #[test]
1202 fn link_target_type_try_from_u8() {
1203 assert_eq!(
1204 LinkTargetType::try_from(0u8).unwrap(),
1205 LinkTargetType::Unknown
1206 );
1207 assert_eq!(LinkTargetType::try_from(1u8).unwrap(), LinkTargetType::File);
1208 assert_eq!(
1209 LinkTargetType::try_from(2u8).unwrap(),
1210 LinkTargetType::Directory
1211 );
1212 assert!(LinkTargetType::try_from(3u8).is_err());
1213 }
1214
1215 #[test]
1216 fn link_target_type_trailing_bytes_ignored() {
1217 assert_eq!(
1219 LinkTargetType::try_from_bytes(&[0x01, 0xFF, 0xFF]).unwrap(),
1220 Some(LinkTargetType::File),
1221 );
1222 }
1223
1224 #[allow(deprecated)]
1225 #[test]
1226 fn all_owner_facets_and_fprm_coexist_round_trip() {
1227 use crate::entry::{
1228 OwnerGid, OwnerGroupName, OwnerGroupSid, OwnerUid, OwnerUserName, OwnerUserSid,
1229 PermissionMode,
1230 };
1231 use crate::{Archive, FileEntryBuilder};
1232 let mut buf = Vec::new();
1233 {
1234 let mut archive = Archive::write_header(&mut buf).unwrap();
1235 let mut b = FileEntryBuilder::new("f".into()).unwrap();
1236 b.metadata(
1237 Metadata::new()
1238 .with_permission(Some(Permission::new(
1240 7,
1241 "legacy".to_string(),
1242 8,
1243 "grp".to_string(),
1244 0o600,
1245 )))
1246 .with_owner_uid(Some(OwnerUid::from(1)))
1248 .with_owner_gid(Some(OwnerGid::from(2)))
1249 .with_owner_user_name(Some(OwnerUserName::new("u").unwrap()))
1250 .with_owner_group_name(Some(OwnerGroupName::new("g").unwrap()))
1251 .with_owner_user_sid(Some(OwnerUserSid::new("S-1-1").unwrap()))
1252 .with_owner_group_sid(Some(OwnerGroupSid::new("S-1-2").unwrap()))
1253 .with_permission_mode(Some(PermissionMode::from(0o644))),
1254 );
1255 let entry = b.build().unwrap();
1256 archive.add_entry(entry).unwrap();
1257 archive.finalize().unwrap();
1258 }
1259 let mut archive = Archive::read_header(&buf[..]).unwrap();
1260 let entry = archive.entries().skip_solid().next().unwrap().unwrap();
1261 let m = entry.metadata();
1262 assert_eq!(m.owner_uid().map(|v| v.get()), Some(1));
1264 assert_eq!(m.owner_gid().map(|v| v.get()), Some(2));
1265 assert_eq!(m.owner_user_name().map(|v| v.as_str()), Some("u"));
1266 assert_eq!(m.owner_group_name().map(|v| v.as_str()), Some("g"));
1267 assert_eq!(m.owner_user_sid().map(|v| v.as_str()), Some("S-1-1"));
1268 assert_eq!(m.owner_group_sid().map(|v| v.as_str()), Some("S-1-2"));
1269 assert_eq!(m.permission_mode().map(|v| v.get()), Some(0o644));
1270 let p = m
1272 .permission()
1273 .expect("fPRM permission must still be present");
1274 assert_eq!(p.uid(), 7);
1275 assert_eq!(p.uname(), "legacy");
1276 assert_eq!(p.gid(), 8);
1277 assert_eq!(p.gname(), "grp");
1278 assert_eq!(p.permissions(), 0o600);
1279 }
1280
1281 #[test]
1282 fn fonm_trailing_bytes_after_length_are_ignored() {
1283 use crate::{Archive, ChunkType, FileEntryBuilder, RawChunk};
1284 let mut buf = Vec::new();
1285 {
1286 let mut a = Archive::write_header(&mut buf).unwrap();
1287 let mut b = FileEntryBuilder::new("g".into()).unwrap();
1288 b.add_extra_chunk(RawChunk::from_data(
1289 ChunkType::fONm,
1290 vec![3, b'a', b'b', b'c', 0xFF],
1291 ));
1292 a.add_entry(b.build().unwrap()).unwrap();
1293 a.finalize().unwrap();
1294 }
1295 let mut a = Archive::read_header(&buf[..]).unwrap();
1296 let e = a.entries().skip_solid().next().unwrap().unwrap();
1297 assert_eq!(
1298 e.metadata().owner_user_name().map(|v| v.as_str()),
1299 Some("abc")
1300 );
1301 }
1302}