@@ -1078,3 +1078,80 @@ 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+ 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