@@ -2,11 +2,13 @@ pub(crate) mod path;
22mod path_filter;
33pub ( crate ) mod path_lock;
44mod path_transformer;
5+ pub ( crate ) mod permission;
56pub ( crate ) mod re;
67pub ( crate ) mod time_filter;
78pub ( crate ) mod timestamp;
89
910pub ( crate ) use self :: path:: PathnameEditor ;
11+ pub ( crate ) use self :: permission:: { OwnerSource , PermissionStrategy } ;
1012pub ( crate ) use self :: timestamp:: { TimeSource , TimestampStrategy } ;
1113use crate :: {
1214 cli:: { CipherAlgorithmArgs , CompressionAlgorithmArgs , HashAlgorithmArgs } ,
@@ -125,24 +127,6 @@ impl XattrStrategy {
125127 }
126128}
127129
128- #[ derive( Copy , Clone , Debug , Eq , PartialEq , Ord , PartialOrd , Hash ) ]
129- pub ( crate ) enum PermissionStrategy {
130- Never ,
131- Always ,
132- }
133-
134- impl PermissionStrategy {
135- pub ( crate ) const fn from_flags ( keep_permission : bool , no_keep_permission : bool ) -> Self {
136- if no_keep_permission {
137- Self :: Never
138- } else if keep_permission {
139- Self :: Always
140- } else {
141- Self :: Never
142- }
143- }
144- }
145-
146130#[ derive( Copy , Clone , Debug , Eq , PartialEq , Ord , PartialOrd , Hash ) ]
147131pub ( crate ) enum AclStrategy {
148132 Never ,
@@ -211,7 +195,7 @@ impl MacMetadataStrategy {
211195 }
212196}
213197
214- #[ derive( Copy , Clone , Debug , Eq , PartialEq , Ord , PartialOrd , Hash ) ]
198+ #[ derive( Clone , Debug , Eq , PartialEq , Ord , PartialOrd , Hash ) ]
215199pub ( crate ) struct KeepOptions {
216200 pub ( crate ) timestamp_strategy : TimestampStrategy ,
217201 pub ( crate ) permission_strategy : PermissionStrategy ,
@@ -270,36 +254,59 @@ impl TimestampStrategyResolver {
270254 }
271255}
272256
273- #[ derive( Clone , Debug , Eq , PartialEq , Ord , PartialOrd , Hash ) ]
274- pub ( crate ) struct OwnerOptions {
257+ /// Resolves CLI permission options into a `PermissionStrategy`.
258+ ///
259+ /// This struct encapsulates the CLI-specific logic for determining permission behavior:
260+ /// - `no_keep_permission` forces `Never`
261+ /// - `keep_permission` enables `Preserve` with ownership handling
262+ /// - `same_owner` controls whether to restore ownership (extraction only)
263+ /// - Owner override fields (uid, gid, uname, gname) are used in both creation and extraction
264+ pub ( crate ) struct PermissionStrategyResolver {
265+ pub ( crate ) keep_permission : bool ,
266+ pub ( crate ) no_keep_permission : bool ,
267+ pub ( crate ) same_owner : bool ,
275268 pub ( crate ) uname : Option < String > ,
276269 pub ( crate ) gname : Option < String > ,
277270 pub ( crate ) uid : Option < u32 > ,
278271 pub ( crate ) gid : Option < u32 > ,
272+ pub ( crate ) numeric_owner : bool ,
279273}
280274
281- impl OwnerOptions {
282- #[ inline]
283- pub ( crate ) fn new (
284- uname : Option < String > ,
285- gname : Option < String > ,
286- uid : Option < u32 > ,
287- gid : Option < u32 > ,
288- numeric_owner : bool ,
289- ) -> Self {
290- Self {
291- uname : if numeric_owner {
292- Some ( String :: new ( ) )
293- } else {
294- uname
295- } ,
296- gname : if numeric_owner {
297- Some ( String :: new ( ) )
298- } else {
299- gname
300- } ,
301- uid,
302- gid,
275+ impl PermissionStrategyResolver {
276+ /// Resolves CLI options to PermissionStrategy.
277+ ///
278+ /// The `same_owner` field controls ownership handling:
279+ /// - `true`: Restore/store ownership (FromSource)
280+ /// - `false`: Skip ownership restoration (NoRestore, extraction only)
281+ ///
282+ /// For creation contexts, pass `same_owner: true` since ownership
283+ /// is always stored when `--keep-permission` is enabled.
284+ pub ( crate ) fn resolve ( self ) -> PermissionStrategy {
285+ if self . no_keep_permission {
286+ PermissionStrategy :: Never
287+ } else if self . keep_permission {
288+ PermissionStrategy :: Preserve {
289+ owner : if self . same_owner {
290+ OwnerSource :: FromSource {
291+ uname : if self . numeric_owner {
292+ Some ( String :: new ( ) )
293+ } else {
294+ self . uname
295+ } ,
296+ gname : if self . numeric_owner {
297+ Some ( String :: new ( ) )
298+ } else {
299+ self . gname
300+ } ,
301+ uid : self . uid ,
302+ gid : self . gid ,
303+ }
304+ } else {
305+ OwnerSource :: NoRestore
306+ } ,
307+ }
308+ } else {
309+ PermissionStrategy :: Never
303310 }
304311 }
305312}
@@ -308,7 +315,6 @@ impl OwnerOptions {
308315pub ( crate ) struct CreateOptions {
309316 pub ( crate ) option : WriteOptions ,
310317 pub ( crate ) keep_options : KeepOptions ,
311- pub ( crate ) owner_options : OwnerOptions ,
312318 pub ( crate ) pathname_editor : PathnameEditor ,
313319}
314320
@@ -638,7 +644,6 @@ pub(crate) fn create_entry(
638644 CreateOptions {
639645 option,
640646 keep_options,
641- owner_options,
642647 pathname_editor,
643648 } : & CreateOptions ,
644649) -> io:: Result < Option < NormalEntry > > {
@@ -656,22 +661,22 @@ pub(crate) fn create_entry(
656661 return Ok ( None ) ;
657662 } ;
658663 let entry = EntryBuilder :: new_hard_link ( entry_name, reference) ?;
659- apply_metadata ( entry, path, keep_options, owner_options , metadata) ?. build ( )
664+ apply_metadata ( entry, path, keep_options, metadata) ?. build ( )
660665 }
661666 StoreAs :: Symlink => {
662667 let source = fs:: read_link ( path) ?;
663668 let reference = pathname_editor. edit_symlink ( & source) ;
664669 let entry = EntryBuilder :: new_symlink ( entry_name, reference) ?;
665- apply_metadata ( entry, path, keep_options, owner_options , metadata) ?. build ( )
670+ apply_metadata ( entry, path, keep_options, metadata) ?. build ( )
666671 }
667672 StoreAs :: File => {
668673 let mut entry = EntryBuilder :: new_file ( entry_name, option) ?;
669674 write_from_path ( & mut entry, path) ?;
670- apply_metadata ( entry, path, keep_options, owner_options , metadata) ?. build ( )
675+ apply_metadata ( entry, path, keep_options, metadata) ?. build ( )
671676 }
672677 StoreAs :: Dir => {
673678 let entry = EntryBuilder :: new_dir ( entry_name) ;
674- apply_metadata ( entry, path, keep_options, owner_options , metadata) ?. build ( )
679+ apply_metadata ( entry, path, keep_options, metadata) ?. build ( )
675680 }
676681 }
677682 . map ( Some )
@@ -704,7 +709,6 @@ pub(crate) fn apply_metadata(
704709 mut entry : EntryBuilder ,
705710 path : & Path ,
706711 keep_options : & KeepOptions ,
707- owner_options : & OwnerOptions ,
708712 meta : & fs:: Metadata ,
709713) -> io:: Result < EntryBuilder > {
710714 if let TimestampStrategy :: Preserve {
@@ -724,24 +728,34 @@ pub(crate) fn apply_metadata(
724728 }
725729 }
726730 #[ cfg( unix) ]
727- if let PermissionStrategy :: Always = keep_options. permission_strategy {
731+ if let PermissionStrategy :: Preserve { ref owner } = keep_options. permission_strategy {
728732 use crate :: utils:: fs:: { Group , User } ;
729733 use std:: os:: unix:: fs:: { MetadataExt , PermissionsExt } ;
730734
731735 let mode = meta. permissions ( ) . mode ( ) as u16 ;
732- let uid = owner_options. uid . unwrap_or ( meta. uid ( ) ) ;
733- let gid = owner_options. gid . unwrap_or ( meta. gid ( ) ) ;
736+ // Extract owner overrides from OwnerSource
737+ let ( o_uid, o_gid, o_uname, o_gname) = match owner {
738+ OwnerSource :: NoRestore => unreachable ! ( "NoRestore is not used during creation" ) ,
739+ OwnerSource :: FromSource {
740+ uid,
741+ gid,
742+ uname,
743+ gname,
744+ } => ( * uid, * gid, uname. as_deref ( ) , gname. as_deref ( ) ) ,
745+ } ;
746+ let uid = o_uid. unwrap_or ( meta. uid ( ) ) ;
747+ let gid = o_gid. unwrap_or ( meta. gid ( ) ) ;
734748 entry. permission ( pna:: Permission :: new (
735749 uid. into ( ) ,
736- match owner_options . uname . as_deref ( ) {
750+ match o_uname {
737751 None => User :: from_uid ( uid. into ( ) ) ?
738752 . name ( )
739753 . unwrap_or_default ( )
740754 . into ( ) ,
741755 Some ( uname) => uname. into ( ) ,
742756 } ,
743757 gid. into ( ) ,
744- match owner_options . gname . as_deref ( ) {
758+ match o_gname {
745759 None => Group :: from_gid ( gid. into ( ) ) ?
746760 . name ( )
747761 . unwrap_or_default ( )
@@ -752,19 +766,29 @@ pub(crate) fn apply_metadata(
752766 ) ) ;
753767 }
754768 #[ cfg( windows) ]
755- if let PermissionStrategy :: Always = keep_options. permission_strategy {
769+ if let PermissionStrategy :: Preserve { ref owner } = keep_options. permission_strategy {
756770 use crate :: utils:: os:: windows:: { fs:: stat, security:: SecurityDescriptor } ;
757771
758772 let sd = SecurityDescriptor :: try_from ( path) ?;
759773 let stat = stat ( sd. path . as_ptr ( ) as _ ) ?;
760774 let mode = stat. st_mode ;
761775 let user = sd. owner_sid ( ) ?;
762776 let group = sd. group_sid ( ) ?;
777+ // Extract owner overrides from OwnerSource
778+ let ( o_uid, o_gid, o_uname, o_gname) = match owner {
779+ OwnerSource :: NoRestore => unreachable ! ( "NoRestore is not used during creation" ) ,
780+ OwnerSource :: FromSource {
781+ uid,
782+ gid,
783+ uname,
784+ gname,
785+ } => ( * uid, * gid, uname. clone ( ) , gname. clone ( ) ) ,
786+ } ;
763787 entry. permission ( pna:: Permission :: new (
764- owner_options . uid . map_or ( u64:: MAX , Into :: into) ,
765- owner_options . uname . clone ( ) . unwrap_or ( user. name ) ,
766- owner_options . gid . map_or ( u64:: MAX , Into :: into) ,
767- owner_options . gname . clone ( ) . unwrap_or ( group. name ) ,
788+ o_uid . map_or ( u64:: MAX , Into :: into) ,
789+ o_uname . unwrap_or ( user. name ) ,
790+ o_gid . map_or ( u64:: MAX , Into :: into) ,
791+ o_gname . unwrap_or ( group. name ) ,
768792 mode,
769793 ) ) ;
770794 }
0 commit comments