@@ -8,7 +8,7 @@ pub(crate) mod time_filter;
88pub ( crate ) mod timestamp;
99
1010pub ( crate ) use self :: path:: PathnameEditor ;
11- pub ( crate ) use self :: permission:: { OwnerSource , PermissionStrategy } ;
11+ pub ( crate ) use self :: permission:: { ModeStrategy , OwnerOptions , OwnerStrategy } ;
1212pub ( crate ) use self :: timestamp:: { TimeSource , TimestampStrategy } ;
1313use crate :: {
1414 cli:: { CipherAlgorithmArgs , CompressionAlgorithmArgs , HashAlgorithmArgs } ,
@@ -199,7 +199,8 @@ impl MacMetadataStrategy {
199199#[ derive( Clone , Debug , Eq , PartialEq , Ord , PartialOrd , Hash ) ]
200200pub ( crate ) struct KeepOptions {
201201 pub ( crate ) timestamp_strategy : TimestampStrategy ,
202- pub ( crate ) permission_strategy : PermissionStrategy ,
202+ pub ( crate ) mode_strategy : ModeStrategy ,
203+ pub ( crate ) owner_strategy : OwnerStrategy ,
203204 pub ( crate ) xattr_strategy : XattrStrategy ,
204205 pub ( crate ) acl_strategy : AclStrategy ,
205206 pub ( crate ) fflags_strategy : FflagsStrategy ,
@@ -255,11 +256,11 @@ impl TimestampStrategyResolver {
255256 }
256257}
257258
258- /// Resolves CLI permission options into a `PermissionStrategy` .
259+ /// Resolves CLI permission options into split mode and owner strategies .
259260///
260261/// This struct encapsulates the CLI-specific logic for determining permission behavior:
261- /// - `no_keep_permission` forces `Never`
262- /// - `keep_permission` enables `Preserve` with ownership handling
262+ /// - `no_keep_permission` forces both mode and owner to `Never`
263+ /// - `keep_permission` enables `Preserve` for mode, and ownership handling via `same_owner`
263264/// - `same_owner` controls whether to restore ownership (extraction only)
264265/// - Owner override fields (uid, gid, uname, gname) are used in both creation and extraction
265266pub ( crate ) struct PermissionStrategyResolver {
@@ -274,21 +275,22 @@ pub(crate) struct PermissionStrategyResolver {
274275}
275276
276277impl PermissionStrategyResolver {
277- /// Resolves CLI options to PermissionStrategy .
278+ /// Resolves CLI options to split (ModeStrategy, OwnerStrategy) .
278279 ///
279280 /// The `same_owner` field controls ownership handling:
280- /// - `true`: Restore/store ownership (FromSource )
281- /// - `false`: Skip ownership restoration (NoRestore, extraction only )
281+ /// - `true`: Restore/store ownership (Preserve with options )
282+ /// - `false`: Skip ownership restoration (Never )
282283 ///
283284 /// For creation contexts, pass `same_owner: true` since ownership
284285 /// is always stored when `--keep-permission` is enabled.
285- pub ( crate ) fn resolve ( self ) -> PermissionStrategy {
286+ pub ( crate ) fn resolve ( self ) -> ( ModeStrategy , OwnerStrategy ) {
286287 if self . no_keep_permission {
287- PermissionStrategy :: Never
288+ ( ModeStrategy :: Never , OwnerStrategy :: Never )
288289 } else if self . keep_permission {
289- PermissionStrategy :: Preserve {
290- owner : if self . same_owner {
291- OwnerSource :: FromSource {
290+ let mode_strategy = ModeStrategy :: Preserve ;
291+ let owner_strategy = if self . same_owner {
292+ OwnerStrategy :: Preserve {
293+ options : OwnerOptions {
292294 uname : if self . numeric_owner {
293295 Some ( String :: new ( ) )
294296 } else {
@@ -301,13 +303,14 @@ impl PermissionStrategyResolver {
301303 } ,
302304 uid : self . uid ,
303305 gid : self . gid ,
304- }
305- } else {
306- OwnerSource :: NoRestore
307- } ,
308- }
306+ } ,
307+ }
308+ } else {
309+ OwnerStrategy :: Never
310+ } ;
311+ ( mode_strategy, owner_strategy)
309312 } else {
310- PermissionStrategy :: Never
313+ ( ModeStrategy :: Never , OwnerStrategy :: Never )
311314 }
312315 }
313316}
@@ -729,69 +732,51 @@ pub(crate) fn apply_metadata(
729732 }
730733 }
731734 #[ cfg( unix) ]
732- if let PermissionStrategy :: Preserve { ref owner } = keep_options. permission_strategy {
735+ if let OwnerStrategy :: Preserve { options } = & keep_options. owner_strategy {
733736 use crate :: utils:: fs:: { Group , User } ;
734737 use std:: os:: unix:: fs:: { MetadataExt , PermissionsExt } ;
735738
736739 let mode = meta. permissions ( ) . mode ( ) as u16 ;
737- // Extract owner overrides from OwnerSource
738- let ( o_uid, o_gid, o_uname, o_gname) = match owner {
739- OwnerSource :: NoRestore => unreachable ! ( "NoRestore is not used during creation" ) ,
740- OwnerSource :: FromSource {
741- uid,
742- gid,
743- uname,
744- gname,
745- } => ( * uid, * gid, uname. as_deref ( ) , gname. as_deref ( ) ) ,
740+ // Get owner info: use overrides from OwnerStrategy if Preserve, else use filesystem values
741+ let uid = options. uid . unwrap_or ( meta. uid ( ) ) ;
742+ let gid = options. gid . unwrap_or ( meta. gid ( ) ) ;
743+ let uname = match & options. uname {
744+ None => User :: from_uid ( uid. into ( ) ) ?
745+ . name ( )
746+ . unwrap_or_default ( )
747+ . into ( ) ,
748+ Some ( uname) => uname. clone ( ) ,
749+ } ;
750+ let gname = match & options. gname {
751+ None => Group :: from_gid ( gid. into ( ) ) ?
752+ . name ( )
753+ . unwrap_or_default ( )
754+ . into ( ) ,
755+ Some ( gname) => gname. clone ( ) ,
746756 } ;
747- let uid = o_uid. unwrap_or ( meta. uid ( ) ) ;
748- let gid = o_gid. unwrap_or ( meta. gid ( ) ) ;
749757 entry. permission ( pna:: Permission :: new (
750758 uid. into ( ) ,
751- match o_uname {
752- None => User :: from_uid ( uid. into ( ) ) ?
753- . name ( )
754- . unwrap_or_default ( )
755- . into ( ) ,
756- Some ( uname) => uname. into ( ) ,
757- } ,
759+ uname,
758760 gid. into ( ) ,
759- match o_gname {
760- None => Group :: from_gid ( gid. into ( ) ) ?
761- . name ( )
762- . unwrap_or_default ( )
763- . into ( ) ,
764- Some ( gname) => gname. into ( ) ,
765- } ,
761+ gname,
766762 mode,
767763 ) ) ;
768764 }
769765 #[ cfg( windows) ]
770- if let PermissionStrategy :: Preserve { ref owner } = keep_options. permission_strategy {
766+ if let OwnerStrategy :: Preserve { options } = & keep_options. owner_strategy {
771767 use crate :: utils:: os:: windows:: { fs:: stat, security:: SecurityDescriptor } ;
772768
773769 let sd = SecurityDescriptor :: try_from ( path) ?;
774770 let stat = stat ( sd. path . as_ptr ( ) as _ ) ?;
775771 let mode = stat. st_mode ;
776772 let user = sd. owner_sid ( ) ?;
777773 let group = sd. group_sid ( ) ?;
778- // Extract owner overrides from OwnerSource
779- let ( o_uid, o_gid, o_uname, o_gname) = match owner {
780- OwnerSource :: NoRestore => unreachable ! ( "NoRestore is not used during creation" ) ,
781- OwnerSource :: FromSource {
782- uid,
783- gid,
784- uname,
785- gname,
786- } => ( * uid, * gid, uname. clone ( ) , gname. clone ( ) ) ,
787- } ;
788- entry. permission ( pna:: Permission :: new (
789- o_uid. map_or ( u64:: MAX , Into :: into) ,
790- o_uname. unwrap_or ( user. name ) ,
791- o_gid. map_or ( u64:: MAX , Into :: into) ,
792- o_gname. unwrap_or ( group. name ) ,
793- mode,
794- ) ) ;
774+ // Get owner info: use overrides from OwnerStrategy
775+ let uid = options. uid . map_or ( u64:: MAX , Into :: into) ;
776+ let gid = options. gid . map_or ( u64:: MAX , Into :: into) ;
777+ let uname = options. uname . clone ( ) . unwrap_or ( user. name ) ;
778+ let gname = options. gname . clone ( ) . unwrap_or ( group. name ) ;
779+ entry. permission ( pna:: Permission :: new ( uid, uname, gid, gname, mode) ) ;
795780 }
796781 // On macOS, when mac_metadata_strategy is Always, AppleDouble packing via copyfile()
797782 // already includes xattrs and ACLs. Skip separate handling to avoid duplication.
0 commit comments