@@ -4101,6 +4101,110 @@ fn test_cp_dest_no_permissions() {
41014101 . stderr_contains ( "denied" ) ;
41024102}
41034103
4104+ /// Test readonly destination behavior with reflink options
4105+ #[ cfg( any( target_os = "linux" , target_os = "macos" ) ) ]
4106+ #[ test]
4107+ fn test_cp_readonly_dest_with_reflink ( ) {
4108+ let ts = TestScenario :: new ( util_name ! ( ) ) ;
4109+ let at = & ts. fixtures ;
4110+
4111+ at. write ( "source.txt" , "source content" ) ;
4112+ at. write ( "readonly_dest_auto.txt" , "original content" ) ;
4113+ at. write ( "readonly_dest_always.txt" , "original content" ) ;
4114+ at. set_readonly ( "readonly_dest_auto.txt" ) ;
4115+ at. set_readonly ( "readonly_dest_always.txt" ) ;
4116+
4117+ // Test reflink=auto
4118+ ts. ucmd ( )
4119+ . args ( & [ "--reflink=auto" , "source.txt" , "readonly_dest_auto.txt" ] )
4120+ . fails ( )
4121+ . stderr_contains ( "readonly_dest_auto.txt" ) ;
4122+
4123+ // Test reflink=always
4124+ ts. ucmd ( )
4125+ . args ( & [ "--reflink=always" , "source.txt" , "readonly_dest_always.txt" ] )
4126+ . fails ( )
4127+ . stderr_contains ( "readonly_dest_always.txt" ) ;
4128+
4129+ assert_eq ! ( at. read( "readonly_dest_auto.txt" ) , "original content" ) ;
4130+ assert_eq ! ( at. read( "readonly_dest_always.txt" ) , "original content" ) ;
4131+ }
4132+
4133+ /// Test readonly destination behavior in recursive directory copy
4134+ #[ test]
4135+ fn test_cp_readonly_dest_recursive ( ) {
4136+ let ts = TestScenario :: new ( util_name ! ( ) ) ;
4137+ let at = & ts. fixtures ;
4138+
4139+ at. mkdir ( "source_dir" ) ;
4140+ at. mkdir ( "dest_dir" ) ;
4141+ at. write ( "source_dir/file.txt" , "source content" ) ;
4142+ at. write ( "dest_dir/file.txt" , "original content" ) ;
4143+ at. set_readonly ( "dest_dir/file.txt" ) ;
4144+
4145+ ts. ucmd ( ) . args ( & [ "-r" , "source_dir" , "dest_dir" ] ) . succeeds ( ) ;
4146+
4147+ assert_eq ! ( at. read( "dest_dir/file.txt" ) , "original content" ) ;
4148+ }
4149+
4150+ /// Test copying to readonly file when another file exists
4151+ #[ test]
4152+ fn test_cp_readonly_dest_with_existing_file ( ) {
4153+ let ts = TestScenario :: new ( util_name ! ( ) ) ;
4154+ let at = & ts. fixtures ;
4155+
4156+ at. write ( "source.txt" , "source content" ) ;
4157+ at. write ( "readonly_dest.txt" , "original content" ) ;
4158+ at. write ( "other_file.txt" , "other content" ) ;
4159+ at. set_readonly ( "readonly_dest.txt" ) ;
4160+
4161+ ts. ucmd ( )
4162+ . args ( & [ "source.txt" , "readonly_dest.txt" ] )
4163+ . fails ( )
4164+ . stderr_contains ( "readonly_dest.txt" )
4165+ . stderr_contains ( "denied" ) ;
4166+
4167+ assert_eq ! ( at. read( "readonly_dest.txt" ) , "original content" ) ;
4168+ assert_eq ! ( at. read( "other_file.txt" ) , "other content" ) ;
4169+ }
4170+
4171+ /// Test readonly source file (should work fine)
4172+ #[ test]
4173+ fn test_cp_readonly_source ( ) {
4174+ let ts = TestScenario :: new ( util_name ! ( ) ) ;
4175+ let at = & ts. fixtures ;
4176+
4177+ at. write ( "readonly_source.txt" , "source content" ) ;
4178+ at. write ( "dest.txt" , "dest content" ) ;
4179+ at. set_readonly ( "readonly_source.txt" ) ;
4180+
4181+ ts. ucmd ( )
4182+ . args ( & [ "readonly_source.txt" , "dest.txt" ] )
4183+ . succeeds ( ) ;
4184+
4185+ assert_eq ! ( at. read( "dest.txt" ) , "source content" ) ;
4186+ }
4187+
4188+ /// Test readonly source and destination (should fail)
4189+ #[ test]
4190+ fn test_cp_readonly_source_and_dest ( ) {
4191+ let ts = TestScenario :: new ( util_name ! ( ) ) ;
4192+ let at = & ts. fixtures ;
4193+
4194+ at. write ( "readonly_source.txt" , "source content" ) ;
4195+ at. write ( "readonly_dest.txt" , "original content" ) ;
4196+ at. set_readonly ( "readonly_source.txt" ) ;
4197+ at. set_readonly ( "readonly_dest.txt" ) ;
4198+
4199+ ts. ucmd ( )
4200+ . args ( & [ "readonly_source.txt" , "readonly_dest.txt" ] )
4201+ . fails ( )
4202+ . stderr_contains ( "readonly_dest.txt" )
4203+ . stderr_contains ( "denied" ) ;
4204+
4205+ assert_eq ! ( at. read( "readonly_dest.txt" ) , "original content" ) ;
4206+ }
4207+
41044208#[ test]
41054209#[ cfg( all( unix, not( target_os = "freebsd" ) , not( target_os = "openbsd" ) ) ) ]
41064210fn test_cp_attributes_only ( ) {
0 commit comments