@@ -1078,3 +1078,80 @@ fn test_rm_recursive_long_path_safe_traversal() {
1078
1078
// Verify the directory is completely removed
1079
1079
assert ! ( !at. dir_exists( "rm_deep" ) ) ;
1080
1080
}
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
+ let stderr = result. stderr_str ( ) ;
1106
+ assert ! (
1107
+ ( stderr. contains( "rm: cannot remove 'a/1': Directory not empty" )
1108
+ && stderr. contains( "rm: cannot remove 'a': Directory not empty" ) )
1109
+ || ( stderr. contains( "rm: cannot remove 'a/1/2': Permission denied" )
1110
+ && stderr. contains( "rm: cannot remove 'b/3': Permission denied" ) )
1111
+ ) ;
1112
+
1113
+ // Should not report 'b' as "Directory not empty"
1114
+ assert ! ( !stderr. contains( "rm: cannot remove 'b': Directory not empty" ) ) ;
1115
+
1116
+ // Check which directories still exist
1117
+ assert ! ( !at. dir_exists( "a/0" ) ) ; // Should be removed
1118
+ assert ! ( at. dir_exists( "a/1" ) ) ; // Should still exist (no execute permission)
1119
+ assert ! ( !at. dir_exists( "a/2" ) ) ; // Should be removed
1120
+ assert ! ( !at. dir_exists( "a/3" ) ) ; // Should be removed
1121
+
1122
+ // Restore execute permission to check b/3
1123
+ scene. ccmd ( "chmod" ) . arg ( "u+x" ) . arg ( "b" ) . succeeds ( ) ;
1124
+ assert ! ( at. dir_exists( "b/3" ) ) ; // Should still exist
1125
+ }
1126
+
1127
+ #[ cfg( all( not( windows) , feature = "chmod" ) ) ]
1128
+ #[ test]
1129
+ fn test_rm_directory_not_writable ( ) {
1130
+ // Test from GNU rm/rm1.sh
1131
+ // Exercise code paths when directories have no write permission
1132
+ let scene = TestScenario :: new ( util_name ! ( ) ) ;
1133
+ let at = & scene. fixtures ;
1134
+
1135
+ // Create directory structure: b/a/p, b/c, b/d
1136
+ at. mkdir_all ( "b/a/p" ) ;
1137
+ at. mkdir ( "b/c" ) ;
1138
+ at. mkdir ( "b/d" ) ;
1139
+
1140
+ // Remove write permission from b/a
1141
+ scene. ccmd ( "chmod" ) . arg ( "ug-w" ) . arg ( "b/a" ) . succeeds ( ) ;
1142
+
1143
+ // Try to remove b recursively - this should fail
1144
+ let result = scene. ucmd ( ) . args ( & [ "-rf" , "b" ] ) . fails ( ) ;
1145
+
1146
+ // Check for expected error messages (either with or without "directory")
1147
+ let stderr = result. stderr_str ( ) ;
1148
+ assert ! (
1149
+ stderr. contains( "rm: cannot remove directory 'b/a/p': Permission denied" )
1150
+ || stderr. contains( "rm: cannot remove 'b/a/p': Permission denied" )
1151
+ ) ;
1152
+
1153
+ // Check which directories still exist
1154
+ assert ! ( at. dir_exists( "b/a/p" ) ) ; // Should still exist (parent not writable)
1155
+ assert ! ( !at. dir_exists( "b/c" ) ) ; // Should be removed
1156
+ assert ! ( !at. dir_exists( "b/d" ) ) ; // Should be removed
1157
+ }
0 commit comments