1use crate::util::bounded::{LengthExceeded, str::BoundedString};
4use crate::{Duration, UnknownValueError};
5use std::io::{self, Read};
6use std::ops::Deref;
7use std::str;
8
9#[allow(deprecated)]
25#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
26pub struct Metadata {
27 pub(crate) raw_file_size: Option<u128>,
28 pub(crate) compressed_size: usize,
29 pub(crate) created: Option<Duration>,
30 pub(crate) modified: Option<Duration>,
31 pub(crate) accessed: Option<Duration>,
32 pub(crate) permission: Option<Permission>,
33 pub(crate) link_target_type: Option<LinkTargetType>,
34 pub(crate) owner_uid: Option<OwnerUid>,
35 pub(crate) owner_gid: Option<OwnerGid>,
36 pub(crate) owner_user_name: Option<OwnerUserName>,
37 pub(crate) owner_group_name: Option<OwnerGroupName>,
38 pub(crate) owner_user_sid: Option<OwnerUserSid>,
39 pub(crate) owner_group_sid: Option<OwnerGroupSid>,
40 pub(crate) permission_mode: Option<PermissionMode>,
41}
42
43impl Metadata {
44 #[inline]
46 pub const fn new() -> Self {
47 Self {
48 raw_file_size: Some(0),
49 compressed_size: 0,
50 created: None,
51 modified: None,
52 accessed: None,
53 permission: None,
54 link_target_type: None,
55 owner_uid: None,
56 owner_gid: None,
57 owner_user_name: None,
58 owner_group_name: None,
59 owner_user_sid: None,
60 owner_group_sid: None,
61 permission_mode: None,
62 }
63 }
64
65 #[inline]
79 pub const fn with_created(mut self, created: Option<Duration>) -> Self {
80 self.created = created;
81 self
82 }
83
84 #[inline]
98 pub const fn with_modified(mut self, modified: Option<Duration>) -> Self {
99 self.modified = modified;
100 self
101 }
102
103 #[inline]
117 pub const fn with_accessed(mut self, accessed: Option<Duration>) -> Self {
118 self.accessed = accessed;
119 self
120 }
121
122 #[deprecated(
124 since = "0.34.0",
125 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"
126 )]
127 #[allow(deprecated)]
128 #[inline]
129 pub fn with_permission(mut self, permission: Option<Permission>) -> Self {
130 self.permission = permission;
131 self
132 }
133
134 #[inline]
136 pub fn with_owner_uid(mut self, value: Option<OwnerUid>) -> Self {
137 self.owner_uid = value;
138 self
139 }
140 #[inline]
142 pub fn with_owner_gid(mut self, value: Option<OwnerGid>) -> Self {
143 self.owner_gid = value;
144 self
145 }
146 #[inline]
148 pub fn with_owner_user_name(mut self, value: Option<OwnerUserName>) -> Self {
149 self.owner_user_name = value;
150 self
151 }
152 #[inline]
154 pub fn with_owner_group_name(mut self, value: Option<OwnerGroupName>) -> Self {
155 self.owner_group_name = value;
156 self
157 }
158 #[inline]
160 pub fn with_owner_user_sid(mut self, value: Option<OwnerUserSid>) -> Self {
161 self.owner_user_sid = value;
162 self
163 }
164 #[inline]
166 pub fn with_owner_group_sid(mut self, value: Option<OwnerGroupSid>) -> Self {
167 self.owner_group_sid = value;
168 self
169 }
170 #[inline]
172 pub fn with_permission_mode(mut self, value: Option<PermissionMode>) -> Self {
173 self.permission_mode = value;
174 self
175 }
176
177 #[inline]
180 pub const fn with_link_target_type(mut self, link_target_type: Option<LinkTargetType>) -> Self {
181 self.link_target_type = link_target_type;
182 self
183 }
184
185 #[inline]
187 pub const fn raw_file_size(&self) -> Option<u128> {
188 self.raw_file_size
189 }
190 #[inline]
192 pub const fn compressed_size(&self) -> usize {
193 self.compressed_size
194 }
195 #[inline]
197 pub const fn created(&self) -> Option<Duration> {
198 self.created
199 }
200 #[inline]
202 pub const fn modified(&self) -> Option<Duration> {
203 self.modified
204 }
205 #[inline]
207 pub const fn accessed(&self) -> Option<Duration> {
208 self.accessed
209 }
210 #[deprecated(
212 since = "0.34.0",
213 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"
214 )]
215 #[allow(deprecated)]
216 #[inline]
217 pub const fn permission(&self) -> Option<&Permission> {
218 self.permission.as_ref()
219 }
220 #[inline]
222 pub const fn owner_uid(&self) -> Option<OwnerUid> {
223 self.owner_uid
224 }
225 #[inline]
227 pub const fn owner_gid(&self) -> Option<OwnerGid> {
228 self.owner_gid
229 }
230 #[inline]
232 pub fn owner_user_name(&self) -> Option<&OwnerUserName> {
233 self.owner_user_name.as_ref()
234 }
235 #[inline]
237 pub fn owner_group_name(&self) -> Option<&OwnerGroupName> {
238 self.owner_group_name.as_ref()
239 }
240 #[inline]
242 pub fn owner_user_sid(&self) -> Option<&OwnerUserSid> {
243 self.owner_user_sid.as_ref()
244 }
245 #[inline]
247 pub fn owner_group_sid(&self) -> Option<&OwnerGroupSid> {
248 self.owner_group_sid.as_ref()
249 }
250 #[inline]
252 pub const fn permission_mode(&self) -> Option<PermissionMode> {
253 self.permission_mode
254 }
255
256 #[inline]
262 pub const fn link_target_type(&self) -> Option<LinkTargetType> {
263 self.link_target_type
264 }
265}
266
267impl Default for Metadata {
268 #[inline]
269 fn default() -> Self {
270 Self::new()
271 }
272}
273
274#[deprecated(
276 since = "0.34.0",
277 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 EntryBuilder/with_* setters)"
278)]
279#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
280pub struct Permission {
281 uid: u64,
282 uname: String,
283 gid: u64,
284 gname: String,
285 permission: u16,
286}
287
288#[allow(deprecated)]
289impl Permission {
290 #[deprecated(
304 since = "0.34.0",
305 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 EntryBuilder/with_* setters)"
306 )]
307 #[inline]
308 pub const fn new(uid: u64, uname: String, gid: u64, gname: String, permission: u16) -> Self {
309 Self {
310 uid,
311 uname,
312 gid,
313 gname,
314 permission,
315 }
316 }
317 #[deprecated(
329 since = "0.34.0",
330 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 EntryBuilder/with_* setters)"
331 )]
332 #[inline]
333 pub const fn uid(&self) -> u64 {
334 self.uid
335 }
336
337 #[deprecated(
349 since = "0.34.0",
350 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 EntryBuilder/with_* setters)"
351 )]
352 #[inline]
353 pub fn uname(&self) -> &str {
354 &self.uname
355 }
356
357 #[deprecated(
369 since = "0.34.0",
370 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 EntryBuilder/with_* setters)"
371 )]
372 #[inline]
373 pub const fn gid(&self) -> u64 {
374 self.gid
375 }
376
377 #[deprecated(
389 since = "0.34.0",
390 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 EntryBuilder/with_* setters)"
391 )]
392 #[inline]
393 pub fn gname(&self) -> &str {
394 &self.gname
395 }
396
397 #[deprecated(
409 since = "0.34.0",
410 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 EntryBuilder/with_* setters)"
411 )]
412 #[inline]
413 pub const fn permissions(&self) -> u16 {
414 self.permission
415 }
416
417 pub(crate) fn to_bytes(&self) -> Vec<u8> {
418 let mut bytes = Vec::with_capacity(20 + self.uname.len() + self.gname.len());
419 bytes.extend_from_slice(&self.uid.to_be_bytes());
420 bytes.extend_from_slice(&(self.uname.len() as u8).to_be_bytes());
421 bytes.extend_from_slice(self.uname.as_bytes());
422 bytes.extend_from_slice(&self.gid.to_be_bytes());
423 bytes.extend_from_slice(&(self.gname.len() as u8).to_be_bytes());
424 bytes.extend_from_slice(self.gname.as_bytes());
425 bytes.extend_from_slice(&self.permission.to_be_bytes());
426 bytes
427 }
428
429 pub(crate) fn try_from_bytes(mut bytes: &[u8]) -> io::Result<Self> {
430 let uid = u64::from_be_bytes({
431 let mut buf = [0; 8];
432 bytes.read_exact(&mut buf)?;
433 buf
434 });
435 let uname_len = {
436 let mut buf = [0; 1];
437 bytes.read_exact(&mut buf)?;
438 buf[0] as usize
439 };
440 let uname = String::from_utf8({
441 let mut buf = vec![0; uname_len];
442 bytes.read_exact(&mut buf)?;
443 buf
444 })
445 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
446 let gid = u64::from_be_bytes({
447 let mut buf = [0; 8];
448 bytes.read_exact(&mut buf)?;
449 buf
450 });
451 let gname_len = {
452 let mut buf = [0; 1];
453 bytes.read_exact(&mut buf)?;
454 buf[0] as usize
455 };
456 let gname = String::from_utf8({
457 let mut buf = vec![0; gname_len];
458 bytes.read_exact(&mut buf)?;
459 buf
460 })
461 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
462 let permission = u16::from_be_bytes({
463 let mut buf = [0; 2];
464 bytes.read_exact(&mut buf)?;
465 buf
466 });
467 Ok(Self {
468 uid,
469 uname,
470 gid,
471 gname,
472 permission,
473 })
474 }
475}
476
477const OWNER_STR_MAX: usize = u8::MAX as usize;
480
481#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
483#[repr(transparent)]
484pub struct OwnerUserName(BoundedString<OWNER_STR_MAX>);
485
486impl OwnerUserName {
487 #[inline]
493 pub fn new(value: impl Into<Box<str>>) -> Result<Self, LengthExceeded> {
494 BoundedString::new(value).map(Self)
495 }
496 #[inline]
498 #[must_use]
499 pub fn as_str(&self) -> &str {
500 self.0.as_str()
501 }
502 pub(crate) fn to_bytes(&self) -> Vec<u8> {
503 let b = self.0.as_str().as_bytes();
504 let mut v = Vec::with_capacity(1 + b.len());
505 v.push(b.len() as u8);
507 v.extend_from_slice(b);
508 v
509 }
510 pub(crate) fn try_from_bytes(bytes: &[u8]) -> io::Result<Self> {
511 let (&len, rest) = bytes.split_first().ok_or(io::ErrorKind::UnexpectedEof)?;
512 let s = rest
513 .get(..len as usize)
514 .ok_or(io::ErrorKind::UnexpectedEof)?;
515 let s = str::from_utf8(s).map_err(|_| io::ErrorKind::InvalidData)?;
516 Self::new(s.to_owned()).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
517 }
518}
519
520impl Deref for OwnerUserName {
521 type Target = str;
522 #[inline]
523 fn deref(&self) -> &str {
524 self.0.as_str()
525 }
526}
527impl TryFrom<String> for OwnerUserName {
528 type Error = LengthExceeded;
529 #[inline]
530 fn try_from(value: String) -> Result<Self, Self::Error> {
531 Self::new(value)
532 }
533}
534impl TryFrom<&str> for OwnerUserName {
535 type Error = LengthExceeded;
536 #[inline]
537 fn try_from(value: &str) -> Result<Self, Self::Error> {
538 Self::new(value)
539 }
540}
541impl From<OwnerUserName> for String {
542 #[inline]
543 fn from(value: OwnerUserName) -> Self {
544 value.0.into()
545 }
546}
547
548#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
550#[repr(transparent)]
551pub struct OwnerGroupName(BoundedString<OWNER_STR_MAX>);
552
553impl OwnerGroupName {
554 #[inline]
560 pub fn new(value: impl Into<Box<str>>) -> Result<Self, LengthExceeded> {
561 BoundedString::new(value).map(Self)
562 }
563 #[inline]
565 #[must_use]
566 pub fn as_str(&self) -> &str {
567 self.0.as_str()
568 }
569 pub(crate) fn to_bytes(&self) -> Vec<u8> {
570 let b = self.0.as_str().as_bytes();
571 let mut v = Vec::with_capacity(1 + b.len());
572 v.push(b.len() as u8);
574 v.extend_from_slice(b);
575 v
576 }
577 pub(crate) fn try_from_bytes(bytes: &[u8]) -> io::Result<Self> {
578 let (&len, rest) = bytes.split_first().ok_or(io::ErrorKind::UnexpectedEof)?;
579 let s = rest
580 .get(..len as usize)
581 .ok_or(io::ErrorKind::UnexpectedEof)?;
582 let s = str::from_utf8(s).map_err(|_| io::ErrorKind::InvalidData)?;
583 Self::new(s.to_owned()).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
584 }
585}
586
587impl Deref for OwnerGroupName {
588 type Target = str;
589 #[inline]
590 fn deref(&self) -> &str {
591 self.0.as_str()
592 }
593}
594impl TryFrom<String> for OwnerGroupName {
595 type Error = LengthExceeded;
596 #[inline]
597 fn try_from(value: String) -> Result<Self, Self::Error> {
598 Self::new(value)
599 }
600}
601impl TryFrom<&str> for OwnerGroupName {
602 type Error = LengthExceeded;
603 #[inline]
604 fn try_from(value: &str) -> Result<Self, Self::Error> {
605 Self::new(value)
606 }
607}
608impl From<OwnerGroupName> for String {
609 #[inline]
610 fn from(value: OwnerGroupName) -> Self {
611 value.0.into()
612 }
613}
614
615#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
617#[repr(transparent)]
618pub struct OwnerUserSid(BoundedString<OWNER_STR_MAX>);
619
620impl OwnerUserSid {
621 #[inline]
627 pub fn new(value: impl Into<Box<str>>) -> Result<Self, LengthExceeded> {
628 BoundedString::new(value).map(Self)
629 }
630 #[inline]
632 #[must_use]
633 pub fn as_str(&self) -> &str {
634 self.0.as_str()
635 }
636 pub(crate) fn to_bytes(&self) -> Vec<u8> {
637 let b = self.0.as_str().as_bytes();
638 let mut v = Vec::with_capacity(1 + b.len());
639 v.push(b.len() as u8);
641 v.extend_from_slice(b);
642 v
643 }
644 pub(crate) fn try_from_bytes(bytes: &[u8]) -> io::Result<Self> {
645 let (&len, rest) = bytes.split_first().ok_or(io::ErrorKind::UnexpectedEof)?;
646 let s = rest
647 .get(..len as usize)
648 .ok_or(io::ErrorKind::UnexpectedEof)?;
649 let s = str::from_utf8(s).map_err(|_| io::ErrorKind::InvalidData)?;
650 Self::new(s.to_owned()).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
651 }
652}
653
654impl Deref for OwnerUserSid {
655 type Target = str;
656 #[inline]
657 fn deref(&self) -> &str {
658 self.0.as_str()
659 }
660}
661impl TryFrom<String> for OwnerUserSid {
662 type Error = LengthExceeded;
663 #[inline]
664 fn try_from(value: String) -> Result<Self, Self::Error> {
665 Self::new(value)
666 }
667}
668impl TryFrom<&str> for OwnerUserSid {
669 type Error = LengthExceeded;
670 #[inline]
671 fn try_from(value: &str) -> Result<Self, Self::Error> {
672 Self::new(value)
673 }
674}
675impl From<OwnerUserSid> for String {
676 #[inline]
677 fn from(value: OwnerUserSid) -> Self {
678 value.0.into()
679 }
680}
681
682#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
684#[repr(transparent)]
685pub struct OwnerGroupSid(BoundedString<OWNER_STR_MAX>);
686
687impl OwnerGroupSid {
688 #[inline]
694 pub fn new(value: impl Into<Box<str>>) -> Result<Self, LengthExceeded> {
695 BoundedString::new(value).map(Self)
696 }
697 #[inline]
699 #[must_use]
700 pub fn as_str(&self) -> &str {
701 self.0.as_str()
702 }
703 pub(crate) fn to_bytes(&self) -> Vec<u8> {
704 let b = self.0.as_str().as_bytes();
705 let mut v = Vec::with_capacity(1 + b.len());
706 v.push(b.len() as u8);
708 v.extend_from_slice(b);
709 v
710 }
711 pub(crate) fn try_from_bytes(bytes: &[u8]) -> io::Result<Self> {
712 let (&len, rest) = bytes.split_first().ok_or(io::ErrorKind::UnexpectedEof)?;
713 let s = rest
714 .get(..len as usize)
715 .ok_or(io::ErrorKind::UnexpectedEof)?;
716 let s = str::from_utf8(s).map_err(|_| io::ErrorKind::InvalidData)?;
717 Self::new(s.to_owned()).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
718 }
719}
720
721impl Deref for OwnerGroupSid {
722 type Target = str;
723 #[inline]
724 fn deref(&self) -> &str {
725 self.0.as_str()
726 }
727}
728impl TryFrom<String> for OwnerGroupSid {
729 type Error = LengthExceeded;
730 #[inline]
731 fn try_from(value: String) -> Result<Self, Self::Error> {
732 Self::new(value)
733 }
734}
735impl TryFrom<&str> for OwnerGroupSid {
736 type Error = LengthExceeded;
737 #[inline]
738 fn try_from(value: &str) -> Result<Self, Self::Error> {
739 Self::new(value)
740 }
741}
742impl From<OwnerGroupSid> for String {
743 #[inline]
744 fn from(value: OwnerGroupSid) -> Self {
745 value.0.into()
746 }
747}
748
749#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
751#[repr(transparent)]
752pub struct OwnerUid(u64);
753
754impl OwnerUid {
755 #[inline]
757 #[must_use]
758 pub const fn get(self) -> u64 {
759 self.0
760 }
761 pub(crate) fn to_bytes(self) -> [u8; 8] {
762 self.0.to_be_bytes()
763 }
764 pub(crate) fn try_from_bytes(bytes: &[u8]) -> io::Result<Self> {
765 let a: [u8; 8] = bytes
766 .try_into()
767 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "fUId must be 8 bytes"))?;
768 Ok(Self(u64::from_be_bytes(a)))
769 }
770}
771impl From<u64> for OwnerUid {
772 #[inline]
773 fn from(v: u64) -> Self {
774 Self(v)
775 }
776}
777
778#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
780#[repr(transparent)]
781pub struct OwnerGid(u64);
782
783impl OwnerGid {
784 #[inline]
786 #[must_use]
787 pub const fn get(self) -> u64 {
788 self.0
789 }
790 pub(crate) fn to_bytes(self) -> [u8; 8] {
791 self.0.to_be_bytes()
792 }
793 pub(crate) fn try_from_bytes(bytes: &[u8]) -> io::Result<Self> {
794 let a: [u8; 8] = bytes
795 .try_into()
796 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "fGId must be 8 bytes"))?;
797 Ok(Self(u64::from_be_bytes(a)))
798 }
799}
800impl From<u64> for OwnerGid {
801 #[inline]
802 fn from(v: u64) -> Self {
803 Self(v)
804 }
805}
806
807#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
810#[repr(transparent)]
811pub struct PermissionMode(u16);
812
813impl PermissionMode {
814 #[inline]
816 #[must_use]
817 pub const fn get(self) -> u16 {
818 self.0
819 }
820 pub(crate) fn to_bytes(self) -> [u8; 2] {
821 self.0.to_be_bytes()
822 }
823 pub(crate) fn try_from_bytes(bytes: &[u8]) -> io::Result<Self> {
824 let a: [u8; 2] = bytes
825 .try_into()
826 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "fMOd must be 2 bytes"))?;
827 Ok(Self::from(u16::from_be_bytes(a)))
828 }
829}
830impl From<u16> for PermissionMode {
831 #[inline]
832 fn from(v: u16) -> Self {
833 Self(v & 0o7777)
834 }
835}
836
837#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
861#[repr(u8)]
862pub enum LinkTargetType {
863 Unknown = 0,
865 File = 1,
867 Directory = 2,
869}
870
871impl TryFrom<u8> for LinkTargetType {
872 type Error = UnknownValueError;
873
874 #[inline]
875 fn try_from(value: u8) -> Result<Self, Self::Error> {
876 match value {
877 0 => Ok(Self::Unknown),
878 1 => Ok(Self::File),
879 2 => Ok(Self::Directory),
880 value => Err(UnknownValueError(value)),
881 }
882 }
883}
884
885impl LinkTargetType {
886 pub(crate) fn to_bytes(self) -> [u8; 1] {
887 [self as u8]
888 }
889
890 pub(crate) fn try_from_bytes(mut bytes: &[u8]) -> io::Result<Option<Self>> {
896 let mut buf = [0u8; 1];
897 bytes.read_exact(&mut buf)?;
898 Ok(Self::try_from(buf[0]).ok())
899 }
900}
901
902#[cfg(test)]
903mod tests {
904 use super::*;
905 #[cfg(all(target_family = "wasm", target_os = "unknown"))]
906 use wasm_bindgen_test::wasm_bindgen_test as test;
907
908 #[allow(deprecated)]
909 #[test]
910 fn permission() {
911 let perm = Permission::new(1000, "user1".into(), 100, "group1".into(), 0o644);
912 assert_eq!(perm, Permission::try_from_bytes(&perm.to_bytes()).unwrap());
913 }
914
915 #[test]
916 fn owner_string_newtype_bound_and_codec() {
917 use crate::entry::OwnerUserName;
918 assert!(OwnerUserName::new("").is_ok());
919 assert!(OwnerUserName::new("alice").is_ok());
920 assert!(OwnerUserName::new("a".repeat(255)).is_ok());
921 assert!(OwnerUserName::new("a".repeat(256)).is_err());
922 let n = OwnerUserName::new("alice").unwrap();
923 assert_eq!(n.to_bytes(), vec![5, b'a', b'l', b'i', b'c', b'e']);
924 assert_eq!(OwnerUserName::try_from_bytes(&n.to_bytes()).unwrap(), n);
925 assert_eq!(OwnerUserName::try_from_bytes(&[0]).unwrap().as_str(), "");
926 assert_eq!(
927 OwnerUserName::try_from_bytes(&[3, b'a', b'b', b'c', 0xFF])
928 .unwrap()
929 .as_str(),
930 "abc"
931 );
932 assert!(OwnerUserName::try_from_bytes(&[]).is_err());
933 assert!(OwnerUserName::try_from_bytes(&[5, b'a']).is_err());
934 assert!(OwnerUserName::try_from_bytes(&[1, 0xFF]).is_err());
935 }
936
937 #[test]
938 fn owner_uid_and_permission_mode_codec() {
939 use crate::entry::{OwnerUid, PermissionMode};
940 let u = OwnerUid::from(1000u64);
941 assert_eq!(u.get(), 1000);
942 assert_eq!(u.to_bytes(), 1000u64.to_be_bytes());
943 assert_eq!(OwnerUid::try_from_bytes(&u.to_bytes()).unwrap(), u);
944 assert!(OwnerUid::try_from_bytes(&[0, 0, 0]).is_err());
945 assert_eq!(PermissionMode::from(0o7777u16).get(), 0o7777);
946 assert_eq!(PermissionMode::from(0o170755u16).get(), 0o0755);
947 let m = PermissionMode::from(0o644u16);
948 assert_eq!(m.to_bytes(), 0o644u16.to_be_bytes());
949 assert_eq!(PermissionMode::try_from_bytes(&m.to_bytes()).unwrap(), m);
950 assert_eq!(
951 PermissionMode::try_from_bytes(&0o170644u16.to_be_bytes())
952 .unwrap()
953 .get(),
954 0o0644
955 );
956 assert!(PermissionMode::try_from_bytes(&[0]).is_err());
957 }
958
959 #[test]
960 fn metadata_owner_facets_default_none() {
961 let m = Metadata::new();
962 assert_eq!(m.owner_uid(), None);
963 assert_eq!(m.owner_gid(), None);
964 assert_eq!(m.owner_user_name(), None);
965 assert_eq!(m.owner_group_name(), None);
966 assert_eq!(m.owner_user_sid(), None);
967 assert_eq!(m.owner_group_sid(), None);
968 assert_eq!(m.permission_mode(), None);
969 }
970
971 #[test]
972 fn owner_id_facets_round_trip_via_entry() {
973 use crate::entry::{OwnerGid, OwnerUid};
974 use crate::{Archive, EntryBuilder, WriteOptions};
975 let mut buf = Vec::new();
976 {
977 let mut archive = Archive::write_header(&mut buf).unwrap();
978 let mut b = EntryBuilder::new_file("f".into(), WriteOptions::store()).unwrap();
979 b.owner_uid(OwnerUid::from(1000));
980 b.owner_gid(OwnerGid::from(2000));
981 let entry = b.build().unwrap();
982 archive.add_entry(entry).unwrap();
983 archive.finalize().unwrap();
984 }
985 let mut archive = Archive::read_header(&buf[..]).unwrap();
986 let entry = archive.entries().skip_solid().next().unwrap().unwrap();
987 let m = entry.metadata();
988 assert_eq!(m.owner_uid().map(|v| v.get()), Some(1000));
989 assert_eq!(m.owner_gid().map(|v| v.get()), Some(2000));
990 }
991
992 #[test]
993 fn owner_name_facets_round_trip_via_entry() {
994 use crate::entry::{OwnerGroupName, OwnerUserName};
995 use crate::{Archive, EntryBuilder, WriteOptions};
996 let mut buf = Vec::new();
997 {
998 let mut archive = Archive::write_header(&mut buf).unwrap();
999 let mut b = EntryBuilder::new_file("f".into(), WriteOptions::store()).unwrap();
1000 b.owner_user_name(OwnerUserName::new("alice").unwrap());
1001 b.owner_group_name(OwnerGroupName::new("").unwrap());
1002 let entry = b.build().unwrap();
1003 archive.add_entry(entry).unwrap();
1004 archive.finalize().unwrap();
1005 }
1006 let mut archive = Archive::read_header(&buf[..]).unwrap();
1007 let entry = archive.entries().skip_solid().next().unwrap().unwrap();
1008 let m = entry.metadata();
1009 assert_eq!(m.owner_user_name().map(|v| v.as_str()), Some("alice"));
1010 assert_eq!(m.owner_group_name().map(|v| v.as_str()), Some("")); }
1012
1013 #[test]
1014 fn owner_sid_facets_round_trip_via_entry() {
1015 use crate::entry::{OwnerGroupSid, OwnerUserSid};
1016 use crate::{Archive, EntryBuilder, WriteOptions};
1017 let mut buf = Vec::new();
1018 {
1019 let mut archive = Archive::write_header(&mut buf).unwrap();
1020 let mut b = EntryBuilder::new_file("f".into(), WriteOptions::store()).unwrap();
1021 b.owner_user_sid(OwnerUserSid::new("S-1-5-21-1-2-3-1001").unwrap());
1022 b.owner_group_sid(OwnerGroupSid::new("S-1-5-32-544").unwrap());
1023 let entry = b.build().unwrap();
1024 archive.add_entry(entry).unwrap();
1025 archive.finalize().unwrap();
1026 }
1027 let mut archive = Archive::read_header(&buf[..]).unwrap();
1028 let entry = archive.entries().skip_solid().next().unwrap().unwrap();
1029 let m = entry.metadata();
1030 assert_eq!(
1031 m.owner_user_sid().map(|v| v.as_str()),
1032 Some("S-1-5-21-1-2-3-1001")
1033 );
1034 assert_eq!(
1035 m.owner_group_sid().map(|v| v.as_str()),
1036 Some("S-1-5-32-544")
1037 );
1038 }
1039
1040 #[test]
1041 fn fosi_length_prefixed_round_trip_and_empty() {
1042 use crate::entry::{OwnerGroupSid, OwnerUserSid};
1043 use crate::{Archive, EntryBuilder, WriteOptions};
1044 let mut buf = Vec::new();
1045 {
1046 let mut a = Archive::write_header(&mut buf).unwrap();
1047 let mut b = EntryBuilder::new_file("f".into(), WriteOptions::store()).unwrap();
1048 b.owner_user_sid(OwnerUserSid::new("S-1-5-21-1-2-3-1001").unwrap());
1049 b.owner_group_sid(OwnerGroupSid::new("").unwrap()); a.add_entry(b.build().unwrap()).unwrap();
1051 a.finalize().unwrap();
1052 }
1053 let mut a = Archive::read_header(&buf[..]).unwrap();
1054 let e = a.entries().skip_solid().next().unwrap().unwrap();
1055 assert_eq!(
1056 e.metadata().owner_user_sid().map(|v| v.as_str()),
1057 Some("S-1-5-21-1-2-3-1001")
1058 );
1059 assert_eq!(e.metadata().owner_group_sid().map(|v| v.as_str()), Some(""));
1060 }
1061
1062 #[test]
1063 fn permission_mode_facet_round_trip_via_entry() {
1064 use crate::entry::PermissionMode;
1065 use crate::{Archive, EntryBuilder, WriteOptions};
1066 let mut buf = Vec::new();
1067 {
1068 let mut archive = Archive::write_header(&mut buf).unwrap();
1069 let mut b = EntryBuilder::new_file("f".into(), WriteOptions::store()).unwrap();
1070 b.permission_mode(PermissionMode::from(0o750));
1071 let entry = b.build().unwrap();
1072 archive.add_entry(entry).unwrap();
1073 archive.finalize().unwrap();
1074 }
1075 let mut archive = Archive::read_header(&buf[..]).unwrap();
1076 let entry = archive.entries().skip_solid().next().unwrap().unwrap();
1077 assert_eq!(
1078 entry.metadata().permission_mode().map(|v| v.get()),
1079 Some(0o750)
1080 );
1081 }
1082
1083 #[test]
1084 fn link_target_type_roundtrip_unknown() {
1085 let ltp = LinkTargetType::Unknown;
1086 assert_eq!(
1087 Some(ltp),
1088 LinkTargetType::try_from_bytes(<p.to_bytes()).unwrap()
1089 );
1090 }
1091
1092 #[test]
1093 fn link_target_type_roundtrip_file() {
1094 let ltp = LinkTargetType::File;
1095 assert_eq!(
1096 Some(ltp),
1097 LinkTargetType::try_from_bytes(<p.to_bytes()).unwrap()
1098 );
1099 }
1100
1101 #[test]
1102 fn link_target_type_roundtrip_directory() {
1103 let ltp = LinkTargetType::Directory;
1104 assert_eq!(
1105 Some(ltp),
1106 LinkTargetType::try_from_bytes(<p.to_bytes()).unwrap()
1107 );
1108 }
1109
1110 #[test]
1111 fn link_target_type_unknown_values_return_none() {
1112 assert_eq!(LinkTargetType::try_from_bytes(&[0x03]).unwrap(), None);
1113 assert_eq!(LinkTargetType::try_from_bytes(&[0xFF]).unwrap(), None);
1114 }
1115
1116 #[test]
1117 fn link_target_type_empty_bytes() {
1118 assert!(LinkTargetType::try_from_bytes(&[]).is_err());
1119 }
1120
1121 #[test]
1122 fn link_target_type_try_from_u8() {
1123 assert_eq!(
1124 LinkTargetType::try_from(0u8).unwrap(),
1125 LinkTargetType::Unknown
1126 );
1127 assert_eq!(LinkTargetType::try_from(1u8).unwrap(), LinkTargetType::File);
1128 assert_eq!(
1129 LinkTargetType::try_from(2u8).unwrap(),
1130 LinkTargetType::Directory
1131 );
1132 assert!(LinkTargetType::try_from(3u8).is_err());
1133 }
1134
1135 #[test]
1136 fn link_target_type_trailing_bytes_ignored() {
1137 assert_eq!(
1139 LinkTargetType::try_from_bytes(&[0x01, 0xFF, 0xFF]).unwrap(),
1140 Some(LinkTargetType::File),
1141 );
1142 }
1143
1144 #[allow(deprecated)]
1145 #[test]
1146 fn all_owner_facets_and_fprm_coexist_round_trip() {
1147 use crate::entry::{
1148 OwnerGid, OwnerGroupName, OwnerGroupSid, OwnerUid, OwnerUserName, OwnerUserSid,
1149 PermissionMode,
1150 };
1151 use crate::{Archive, EntryBuilder, WriteOptions};
1152 let mut buf = Vec::new();
1153 {
1154 let mut archive = Archive::write_header(&mut buf).unwrap();
1155 let mut b = EntryBuilder::new_file("f".into(), WriteOptions::store()).unwrap();
1156 b.permission(Permission::new(
1158 7,
1159 "legacy".to_string(),
1160 8,
1161 "grp".to_string(),
1162 0o600,
1163 ));
1164 b.owner_uid(OwnerUid::from(1));
1166 b.owner_gid(OwnerGid::from(2));
1167 b.owner_user_name(OwnerUserName::new("u").unwrap());
1168 b.owner_group_name(OwnerGroupName::new("g").unwrap());
1169 b.owner_user_sid(OwnerUserSid::new("S-1-1").unwrap());
1170 b.owner_group_sid(OwnerGroupSid::new("S-1-2").unwrap());
1171 b.permission_mode(PermissionMode::from(0o644));
1172 let entry = b.build().unwrap();
1173 archive.add_entry(entry).unwrap();
1174 archive.finalize().unwrap();
1175 }
1176 let mut archive = Archive::read_header(&buf[..]).unwrap();
1177 let entry = archive.entries().skip_solid().next().unwrap().unwrap();
1178 let m = entry.metadata();
1179 assert_eq!(m.owner_uid().map(|v| v.get()), Some(1));
1181 assert_eq!(m.owner_gid().map(|v| v.get()), Some(2));
1182 assert_eq!(m.owner_user_name().map(|v| v.as_str()), Some("u"));
1183 assert_eq!(m.owner_group_name().map(|v| v.as_str()), Some("g"));
1184 assert_eq!(m.owner_user_sid().map(|v| v.as_str()), Some("S-1-1"));
1185 assert_eq!(m.owner_group_sid().map(|v| v.as_str()), Some("S-1-2"));
1186 assert_eq!(m.permission_mode().map(|v| v.get()), Some(0o644));
1187 let p = m
1189 .permission()
1190 .expect("fPRM permission must still be present");
1191 assert_eq!(p.uid(), 7);
1192 assert_eq!(p.uname(), "legacy");
1193 assert_eq!(p.gid(), 8);
1194 assert_eq!(p.gname(), "grp");
1195 assert_eq!(p.permissions(), 0o600);
1196 }
1197
1198 #[test]
1199 fn fonm_trailing_bytes_after_length_are_ignored() {
1200 use crate::{Archive, ChunkType, EntryBuilder, RawChunk, WriteOptions};
1201 let mut buf = Vec::new();
1202 {
1203 let mut a = Archive::write_header(&mut buf).unwrap();
1204 let mut b = EntryBuilder::new_file("g".into(), WriteOptions::store()).unwrap();
1205 b.add_extra_chunk(RawChunk::from_data(
1206 ChunkType::fONm,
1207 vec![3, b'a', b'b', b'c', 0xFF],
1208 ));
1209 a.add_entry(b.build().unwrap()).unwrap();
1210 a.finalize().unwrap();
1211 }
1212 let mut a = Archive::read_header(&buf[..]).unwrap();
1213 let e = a.entries().skip_solid().next().unwrap().unwrap();
1214 assert_eq!(
1215 e.metadata().owner_user_name().map(|v| v.as_str()),
1216 Some("abc")
1217 );
1218 }
1219}