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