1#!/bin/sh 2# Exercise cp --link's behavior regarding the dereferencing of symbolic links. 3 4# Copyright (C) 2013-2023 Free Software Foundation, Inc. 5 6# This program is free software: you can redistribute it and/or modify 7# it under the terms of the GNU General Public License as published by 8# the Free Software Foundation, either version 3 of the License, or 9# (at your option) any later version. 10 11# This program is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14# GNU General Public License for more details. 15 16# You should have received a copy of the GNU General Public License 17# along with this program. If not, see <https://www.gnu.org/licenses/>. 18 19. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src 20print_ver_ cp 21 22if { grep '^#define HAVE_LINKAT 1' "$CONFIG_HEADER" > /dev/null \ 23 && grep '#undef LINKAT_SYMLINK_NOTSUP' "$CONFIG_HEADER" > /dev/null; } \ 24 || grep '^#define LINK_FOLLOWS_SYMLINKS 0' "$CONFIG_HEADER" > /dev/null; then 25 # With this config cp will attempt to linkat() to hardlink a symlink. 26 # So now double check the current file system supports this operation. 27 ln -s testtarget test_sl || framework_failure_ 28 ln -P test_sl test_hl_sl || framework_failure_ 29 ino_sl="$(stat -c '%i' test_sl)" || framework_failure_ 30 ino_hl="$(stat -c '%i' test_hl_sl)" || framework_failure_ 31 test "$ino_sl" = "$ino_hl" && can_hardlink_to_symlink=1 32fi 33 34mkdir dir || framework_failure_ 35> file || framework_failure_ 36ln -s dir dirlink || framework_failure_ 37ln -s file filelink || framework_failure_ 38ln -s nowhere danglink || framework_failure_ 39 40# printf format of the output line. 41outformat='%s|result=%s|inode=%s|type=%s|error=%s\n' 42 43for src in dirlink filelink danglink; do 44 # Get symlink's target. 45 tgt=$(readlink $src) || framework_failure_ 46 # Get inodes and file type of the symlink (src) and its target (tgt). 47 # Note: this will fail for 'danglink'; catch it. 48 ino_src="$(stat -c '%i' $src)" || framework_failure_ 49 typ_src="$(stat -c '%F' $src)" || framework_failure_ 50 ino_tgt="$(stat -c '%i' $tgt 2>/dev/null)" || ino_tgt= 51 typ_tgt="$(stat -c '%F' $tgt 2>/dev/null)" || typ_tgt= 52 53 for o in '' -L -H -P; do 54 55 # Skip the -P case where we don't or can't hardlink symlinks 56 ! test "$can_hardlink_to_symlink" && test "$o" = '-P' && continue 57 58 for r in '' -R; do 59 60 command="cp --link $o $r $src dst" 61 $command 2> err 62 result=$? 63 64 # Get inode and file type of the destination (which may fail, too). 65 ino_dst="$(stat -c '%i' dst 2>/dev/null)" || ini_dst= 66 typ_dst="$(stat -c '%F' dst 2>/dev/null)" || typ_dst= 67 68 # Print the actual result in a certain format. 69 printf "$outformat" \ 70 "$command" \ 71 "$result" \ 72 "$ino_dst" \ 73 "$typ_dst" \ 74 "$(cat err)" \ 75 > out 76 77 # What was expected? 78 if [ "$o" = "-P" ]; then 79 # cp --link should not dereference if -P is given. 80 exp_result=0 81 exp_inode=$ino_src 82 exp_ftype=$typ_src 83 exp_error= 84 elif [ "$src" = 'danglink' ]; then 85 # Dereferencing should fail for the 'danglink'. 86 exp_result=1 87 exp_inode= 88 exp_ftype= 89 exp_error="cp: cannot stat 'danglink': No such file or directory" 90 elif [ "$src" = 'dirlink' ] && [ "$r" != '-R' ]; then 91 # Dereferencing should fail for the 'dirlink' without -R. 92 exp_result=1 93 exp_inode= 94 exp_ftype= 95 exp_error="cp: -r not specified; omitting directory 'dirlink'" 96 elif [ "$src" = 'dirlink' ]; then 97 # cp --link -R 'dirlink' should create a new directory. 98 exp_result=0 99 exp_inode=$ino_dst 100 exp_ftype=$typ_dst 101 exp_error= 102 else 103 # cp --link 'filelink' should create a hard link to the target. 104 exp_result=0 105 exp_inode=$ino_tgt 106 exp_ftype=$typ_tgt 107 exp_error= 108 fi 109 110 # Print the expected result in a certain format. 111 printf "$outformat" \ 112 "$command" \ 113 "$exp_result" \ 114 "$exp_inode" \ 115 "$exp_ftype" \ 116 "$exp_error" \ 117 > exp 118 119 compare exp out || { ls -lid $src $tgt dst; fail=1; } 120 121 rm -rf dst err exp out || framework_failure_ 122 done 123 done 124done 125 126Exit $fail 127