@@ -1027,3 +1027,167 @@ fn test_chmod_traverse_symlink_combo() {
1027
1027
) ;
1028
1028
}
1029
1029
}
1030
+
1031
+ #[ test]
1032
+ fn test_chmod_recursive_symlink_to_directory_command_line ( ) {
1033
+ // Test behavior when the symlink itself is a command-line argument
1034
+ let scenarios = [
1035
+ ( vec ! [ "-R" ] , true ) , // Default behavior (-H): follow symlinks that are command line args
1036
+ ( vec ! [ "-R" , "-H" ] , true ) , // Explicit -H: follow symlinks that are command line args
1037
+ ( vec ! [ "-R" , "-L" ] , true ) , // -L: follow all symlinks
1038
+ ( vec ! [ "-R" , "-P" ] , false ) , // -P: never follow symlinks
1039
+ ] ;
1040
+
1041
+ for ( flags, should_follow_symlink_dir) in scenarios {
1042
+ let scene = TestScenario :: new ( util_name ! ( ) ) ;
1043
+ let at = & scene. fixtures ;
1044
+
1045
+ let target_dir = "target_dir" ;
1046
+ let symlink_to_dir = "link_dir" ;
1047
+ let file_in_target = "file_in_target" ;
1048
+
1049
+ at. mkdir ( target_dir) ;
1050
+ at. touch ( format ! ( "{target_dir}/{file_in_target}" ) ) ;
1051
+ at. symlink_dir ( target_dir, symlink_to_dir) ;
1052
+
1053
+ set_permissions (
1054
+ at. plus ( format ! ( "{target_dir}/{file_in_target}" ) ) ,
1055
+ Permissions :: from_mode ( 0o644 ) ,
1056
+ )
1057
+ . unwrap ( ) ;
1058
+
1059
+ let mut ucmd = scene. ucmd ( ) ;
1060
+ for f in & flags {
1061
+ ucmd. arg ( f) ;
1062
+ }
1063
+ ucmd. arg ( "go-rwx" )
1064
+ . arg ( symlink_to_dir) // The symlink itself is the command-line argument
1065
+ . succeeds ( )
1066
+ . no_stderr ( ) ;
1067
+
1068
+ let actual_file_perms = at
1069
+ . metadata ( & format ! ( "{target_dir}/{file_in_target}" ) )
1070
+ . permissions ( )
1071
+ . mode ( ) ;
1072
+
1073
+ if should_follow_symlink_dir {
1074
+ // When following symlinks, the file inside the target directory should have its permissions changed
1075
+ assert_eq ! (
1076
+ actual_file_perms, 0o100_600 ,
1077
+ "For flags {flags:?}, expected file perms when following symlinks = 600, got = {actual_file_perms:o}" ,
1078
+ ) ;
1079
+ } else {
1080
+ // When not following symlinks, the file inside the target directory should be unchanged
1081
+ assert_eq ! (
1082
+ actual_file_perms, 0o100_644 ,
1083
+ "For flags {flags:?}, expected file perms when not following symlinks = 644, got = {actual_file_perms:o}" ,
1084
+ ) ;
1085
+ }
1086
+ }
1087
+ }
1088
+
1089
+ #[ test]
1090
+ fn test_chmod_recursive_symlink_during_traversal ( ) {
1091
+ // Test behavior when symlinks are encountered during directory traversal
1092
+ let scenarios = [
1093
+ ( vec ! [ "-R" ] , false ) , // Default behavior (-H): don't follow symlinks encountered during traversal
1094
+ ( vec ! [ "-R" , "-H" ] , false ) , // Explicit -H: don't follow symlinks encountered during traversal
1095
+ ( vec ! [ "-R" , "-L" ] , true ) , // -L: follow all symlinks including those found during traversal
1096
+ ( vec ! [ "-R" , "-P" ] , false ) , // -P: never follow symlinks
1097
+ ] ;
1098
+
1099
+ for ( flags, should_follow_symlink_dir) in scenarios {
1100
+ let scene = TestScenario :: new ( util_name ! ( ) ) ;
1101
+ let at = & scene. fixtures ;
1102
+
1103
+ let directory = "dir" ;
1104
+ let target_dir = "target_dir" ;
1105
+ let symlink_to_dir = "link_dir" ;
1106
+ let file_in_target = "file_in_target" ;
1107
+
1108
+ at. mkdir ( directory) ;
1109
+ at. mkdir ( target_dir) ;
1110
+ at. touch ( format ! ( "{target_dir}/{file_in_target}" ) ) ;
1111
+ at. symlink_dir ( target_dir, & format ! ( "{directory}/{symlink_to_dir}" ) ) ;
1112
+
1113
+ set_permissions (
1114
+ at. plus ( format ! ( "{target_dir}/{file_in_target}" ) ) ,
1115
+ Permissions :: from_mode ( 0o644 ) ,
1116
+ )
1117
+ . unwrap ( ) ;
1118
+
1119
+ let mut ucmd = scene. ucmd ( ) ;
1120
+ for f in & flags {
1121
+ ucmd. arg ( f) ;
1122
+ }
1123
+ ucmd. arg ( "go-rwx" )
1124
+ . arg ( directory) // The directory is the command-line argument
1125
+ . succeeds ( )
1126
+ . no_stderr ( ) ;
1127
+
1128
+ let actual_file_perms = at
1129
+ . metadata ( & format ! ( "{target_dir}/{file_in_target}" ) )
1130
+ . permissions ( )
1131
+ . mode ( ) ;
1132
+
1133
+ if should_follow_symlink_dir {
1134
+ // When following symlinks, the file inside the target directory should have its permissions changed
1135
+ assert_eq ! (
1136
+ actual_file_perms, 0o100_600 ,
1137
+ "For flags {flags:?}, expected file perms when following symlinks = 600, got = {actual_file_perms:o}" ,
1138
+ ) ;
1139
+ } else {
1140
+ // When not following symlinks, the file inside the target directory should be unchanged
1141
+ assert_eq ! (
1142
+ actual_file_perms, 0o100_644 ,
1143
+ "For flags {flags:?}, expected file perms when not following symlinks = 644, got = {actual_file_perms:o}" ,
1144
+ ) ;
1145
+ }
1146
+ }
1147
+ }
1148
+
1149
+ #[ test]
1150
+ fn test_chmod_recursive_symlink_combinations ( ) {
1151
+ let scene = TestScenario :: new ( util_name ! ( ) ) ;
1152
+ let at = & scene. fixtures ;
1153
+
1154
+ let directory = "dir" ;
1155
+ let target_dir = "target_dir" ;
1156
+ let target_file = "target_file" ;
1157
+ let symlink_to_dir = "link_dir" ;
1158
+ let symlink_to_file = "link_file" ;
1159
+ let file_in_target = "file" ;
1160
+
1161
+ at. mkdir ( directory) ;
1162
+ at. mkdir ( target_dir) ;
1163
+ at. touch ( target_file) ;
1164
+ at. touch ( format ! ( "{target_dir}/{file_in_target}" ) ) ;
1165
+ at. symlink_dir ( target_dir, & format ! ( "{directory}/{symlink_to_dir}" ) ) ;
1166
+ at. symlink_file ( target_file, & format ! ( "{directory}/{symlink_to_file}" ) ) ;
1167
+
1168
+ set_permissions ( at. plus ( target_file) , Permissions :: from_mode ( 0o644 ) ) . unwrap ( ) ;
1169
+ set_permissions (
1170
+ at. plus ( format ! ( "{target_dir}/{file_in_target}" ) ) ,
1171
+ Permissions :: from_mode ( 0o644 ) ,
1172
+ )
1173
+ . unwrap ( ) ;
1174
+
1175
+ // Test with -R -L (follow all symlinks)
1176
+ scene
1177
+ . ucmd ( )
1178
+ . arg ( "-R" )
1179
+ . arg ( "-L" )
1180
+ . arg ( "go-rwx" )
1181
+ . arg ( directory)
1182
+ . succeeds ( )
1183
+ . no_stderr ( ) ;
1184
+
1185
+ // Both target file and file in target directory should have permissions changed
1186
+ assert_eq ! ( at. metadata( target_file) . permissions( ) . mode( ) , 0o100_600 ) ;
1187
+ assert_eq ! (
1188
+ at. metadata( & format!( "{target_dir}/{file_in_target}" ) )
1189
+ . permissions( )
1190
+ . mode( ) ,
1191
+ 0o100_600
1192
+ ) ;
1193
+ }
0 commit comments