@@ -5307,3 +5307,138 @@ mod same_file {
53075307 assert_eq ! ( at. read( FILE_NAME ) , CONTENTS , ) ;
53085308 }
53095309}
5310+
5311+ // the following tests are for how the cp should behave when the source is a symlink
5312+ // and link option is given
5313+ #[ cfg( all( unix, not( target_os = "android" ) ) ) ]
5314+ mod link_deref {
5315+
5316+ use crate :: common:: util:: { AtPath , TestScenario } ;
5317+ use std:: os:: unix:: fs:: MetadataExt ;
5318+
5319+ const FILE : & str = "file" ;
5320+ const FILE_LINK : & str = "file_link" ;
5321+ const DIR : & str = "dir" ;
5322+ const DIR_LINK : & str = "dir_link" ;
5323+ const DANG_LINK : & str = "dang_link" ;
5324+ const DST : & str = "dst" ;
5325+
5326+ fn setup_link_deref_tests ( source : & str , at : & AtPath ) {
5327+ match source {
5328+ FILE_LINK => {
5329+ at. touch ( FILE ) ;
5330+ at. symlink_file ( FILE , FILE_LINK ) ;
5331+ }
5332+ DIR_LINK => {
5333+ at. mkdir ( DIR ) ;
5334+ at. symlink_dir ( DIR , DIR_LINK ) ;
5335+ }
5336+ DANG_LINK => at. symlink_file ( "nowhere" , DANG_LINK ) ,
5337+ _ => { }
5338+ }
5339+ }
5340+
5341+ // cp --link shouldn't deref source if -P is given
5342+ #[ test]
5343+ fn test_cp_symlink_as_source_with_link_and_no_deref ( ) {
5344+ for src in [ FILE_LINK , DIR_LINK , DANG_LINK ] {
5345+ for r in [ false , true ] {
5346+ let scene = TestScenario :: new ( util_name ! ( ) ) ;
5347+ let at = & scene. fixtures ;
5348+ setup_link_deref_tests ( src, at) ;
5349+ let mut args = vec ! [ "--link" , "-P" , src, DST ] ;
5350+ if r {
5351+ args. push ( "-R" ) ;
5352+ } ;
5353+ scene. ucmd ( ) . args ( & args) . succeeds ( ) . no_stderr ( ) ;
5354+ at. is_symlink ( DST ) ;
5355+ let src_ino = at. symlink_metadata ( src) . ino ( ) ;
5356+ let dest_ino = at. symlink_metadata ( DST ) . ino ( ) ;
5357+ assert_eq ! ( src_ino, dest_ino) ;
5358+ }
5359+ }
5360+ }
5361+
5362+ // Dereferencing should fail for dangling symlink.
5363+ #[ test]
5364+ fn test_cp_dang_link_as_source_with_link ( ) {
5365+ for option in [ "" , "-L" , "-H" ] {
5366+ for r in [ false , true ] {
5367+ let scene = TestScenario :: new ( util_name ! ( ) ) ;
5368+ let at = & scene. fixtures ;
5369+ setup_link_deref_tests ( DANG_LINK , at) ;
5370+ let mut args = vec ! [ "--link" , DANG_LINK , DST ] ;
5371+ if r {
5372+ args. push ( "-R" ) ;
5373+ } ;
5374+ if !option. is_empty ( ) {
5375+ args. push ( option) ;
5376+ }
5377+ scene
5378+ . ucmd ( )
5379+ . args ( & args)
5380+ . fails ( )
5381+ . stderr_contains ( "No such file or directory" ) ;
5382+ }
5383+ }
5384+ }
5385+
5386+ // Dereferencing should fail for the 'dir_link' without -R.
5387+ #[ test]
5388+ fn test_cp_dir_link_as_source_with_link ( ) {
5389+ for option in [ "" , "-L" , "-H" ] {
5390+ let scene = TestScenario :: new ( util_name ! ( ) ) ;
5391+ let at = & scene. fixtures ;
5392+ setup_link_deref_tests ( DIR_LINK , at) ;
5393+ let mut args = vec ! [ "--link" , DIR_LINK , DST ] ;
5394+ if !option. is_empty ( ) {
5395+ args. push ( option) ;
5396+ }
5397+ scene
5398+ . ucmd ( )
5399+ . args ( & args)
5400+ . fails ( )
5401+ . stderr_contains ( "cp: -r not specified; omitting directory" ) ;
5402+ }
5403+ }
5404+
5405+ // cp --link -R 'dir_link' should create a new directory.
5406+ #[ test]
5407+ fn test_cp_dir_link_as_source_with_link_and_r ( ) {
5408+ for option in [ "" , "-L" , "-H" ] {
5409+ let scene = TestScenario :: new ( util_name ! ( ) ) ;
5410+ let at = & scene. fixtures ;
5411+ setup_link_deref_tests ( DIR_LINK , at) ;
5412+ let mut args = vec ! [ "--link" , "-R" , DIR_LINK , DST ] ;
5413+ if !option. is_empty ( ) {
5414+ args. push ( option) ;
5415+ }
5416+ scene. ucmd ( ) . args ( & args) . succeeds ( ) ;
5417+ at. dir_exists ( DST ) ;
5418+ }
5419+ }
5420+
5421+ //cp --link 'file_link' should create a hard link to the target.
5422+ #[ test]
5423+ fn test_cp_file_link_as_source_with_link ( ) {
5424+ for option in [ "" , "-L" , "-H" ] {
5425+ for r in [ false , true ] {
5426+ let scene = TestScenario :: new ( util_name ! ( ) ) ;
5427+ let at = & scene. fixtures ;
5428+ setup_link_deref_tests ( FILE_LINK , at) ;
5429+ let mut args = vec ! [ "--link" , "-R" , FILE_LINK , DST ] ;
5430+ if !option. is_empty ( ) {
5431+ args. push ( option) ;
5432+ }
5433+ if r {
5434+ args. push ( "-R" ) ;
5435+ }
5436+ scene. ucmd ( ) . args ( & args) . succeeds ( ) ;
5437+ at. file_exists ( DST ) ;
5438+ let src_ino = at. symlink_metadata ( FILE ) . ino ( ) ;
5439+ let dest_ino = at. symlink_metadata ( DST ) . ino ( ) ;
5440+ assert_eq ! ( src_ino, dest_ino) ;
5441+ }
5442+ }
5443+ }
5444+ }
0 commit comments