@@ -1078,3 +1078,74 @@ fn test_rm_recursive_long_path_safe_traversal() {
10781078 // Verify the directory is completely removed
10791079 assert ! ( !at. dir_exists( "rm_deep" ) ) ;
10801080}
1081+
1082+ #[ cfg( all( not( windows) , feature = "chmod" ) ) ]
1083+ #[ test]
1084+ fn test_rm_directory_not_executable ( ) {
1085+ // Test from GNU rm/rm2.sh
1086+ // Exercise code paths when directories have no execute permission
1087+ let scene = TestScenario :: new ( util_name ! ( ) ) ;
1088+ let at = & scene. fixtures ;
1089+
1090+ // Create directory structure: a/0, a/1/2, a/2, a/3, b/3
1091+ at. mkdir_all ( "a/0" ) ;
1092+ at. mkdir_all ( "a/1/2" ) ;
1093+ at. mkdir ( "a/2" ) ;
1094+ at. mkdir ( "a/3" ) ;
1095+ at. mkdir_all ( "b/3" ) ;
1096+
1097+ // Remove execute permission from a/1 and b
1098+ scene. ccmd ( "chmod" ) . arg ( "u-x" ) . arg ( "a/1" ) . succeeds ( ) ;
1099+ scene. ccmd ( "chmod" ) . arg ( "u-x" ) . arg ( "b" ) . succeeds ( ) ;
1100+
1101+ // Try to remove both directories recursively - this should fail
1102+ let result = scene. ucmd ( ) . args ( & [ "-rf" , "a" , "b" ] ) . fails ( ) ;
1103+
1104+ // Check for expected error messages
1105+ // When directories don't have execute permission, we get "Permission denied"
1106+ // when trying to access subdirectories
1107+ let stderr = result. stderr_str ( ) ;
1108+ assert ! ( stderr. contains( "rm: cannot remove 'a/1/2': Permission denied" ) ) ;
1109+ assert ! ( stderr. contains( "rm: cannot remove 'b/3': Permission denied" ) ) ;
1110+
1111+ // Check which directories still exist
1112+ assert ! ( !at. dir_exists( "a/0" ) ) ; // Should be removed
1113+ assert ! ( at. dir_exists( "a/1" ) ) ; // Should still exist (no execute permission)
1114+ assert ! ( !at. dir_exists( "a/2" ) ) ; // Should be removed
1115+ assert ! ( !at. dir_exists( "a/3" ) ) ; // Should be removed
1116+
1117+ // Restore execute permission to check b/3
1118+ scene. ccmd ( "chmod" ) . arg ( "u+x" ) . arg ( "b" ) . succeeds ( ) ;
1119+ assert ! ( at. dir_exists( "b/3" ) ) ; // Should still exist
1120+ }
1121+
1122+ #[ cfg( all( not( windows) , feature = "chmod" ) ) ]
1123+ #[ test]
1124+ fn test_rm_directory_not_writable ( ) {
1125+ // Test from GNU rm/rm1.sh
1126+ // Exercise code paths when directories have no write permission
1127+ let scene = TestScenario :: new ( util_name ! ( ) ) ;
1128+ let at = & scene. fixtures ;
1129+
1130+ // Create directory structure: b/a/p, b/c, b/d
1131+ at. mkdir_all ( "b/a/p" ) ;
1132+ at. mkdir ( "b/c" ) ;
1133+ at. mkdir ( "b/d" ) ;
1134+
1135+ // Remove write permission from b/a
1136+ scene. ccmd ( "chmod" ) . arg ( "ug-w" ) . arg ( "b/a" ) . succeeds ( ) ;
1137+
1138+ // Try to remove b recursively - this should fail
1139+ let result = scene. ucmd ( ) . args ( & [ "-rf" , "b" ] ) . fails ( ) ;
1140+
1141+ // Check for expected error message
1142+ // When the parent directory (b/a) doesn't have write permission,
1143+ // we get "Permission denied" when trying to remove the subdirectory
1144+ let stderr = result. stderr_str ( ) ;
1145+ assert ! ( stderr. contains( "rm: cannot remove 'b/a/p': Permission denied" ) ) ;
1146+
1147+ // Check which directories still exist
1148+ assert ! ( at. dir_exists( "b/a/p" ) ) ; // Should still exist (parent not writable)
1149+ assert ! ( !at. dir_exists( "b/c" ) ) ; // Should be removed
1150+ assert ! ( !at. dir_exists( "b/d" ) ) ; // Should be removed
1151+ }
0 commit comments