1# 2# SPDX-License-Identifier: BSD-2-Clause 3# 4# Copyright (c) 2020 Kyle Evans <kevans@FreeBSD.org> 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 1. Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# 2. Redistributions in binary form must reproduce the above copyright 12# notice, this list of conditions and the following disclaimer in the 13# documentation and/or other materials provided with the distribution. 14# 15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25# SUCH DAMAGE. 26# 27 28check_size() 29{ 30 file=$1 31 sz=$2 32 33 atf_check -o inline:"$sz\n" stat -f '%z' $file 34} 35 36atf_test_case basic 37basic_body() 38{ 39 echo "foo" > bar 40 41 atf_check cp bar baz 42 check_size baz 4 43} 44 45atf_test_case basic_symlink 46basic_symlink_body() 47{ 48 echo "foo" > bar 49 ln -s bar baz 50 51 atf_check cp baz foo 52 atf_check test '!' -L foo 53 54 atf_check cmp foo bar 55} 56 57atf_test_case chrdev 58chrdev_body() 59{ 60 echo "foo" > bar 61 62 check_size bar 4 63 atf_check cp /dev/null trunc 64 check_size trunc 0 65 atf_check cp bar trunc 66 check_size trunc 4 67 atf_check cp /dev/null trunc 68 check_size trunc 0 69} 70 71atf_test_case hardlink 72hardlink_body() 73{ 74 echo "foo" >foo 75 atf_check cp -l foo bar 76 atf_check -o inline:"foo\n" cat bar 77 atf_check_equal "$(stat -f%d,%i foo)" "$(stat -f%d,%i bar)" 78} 79 80atf_test_case hardlink_exists 81hardlink_exists_body() 82{ 83 echo "foo" >foo 84 echo "bar" >bar 85 atf_check -s not-exit:0 -e match:exists cp -l foo bar 86 atf_check -o inline:"bar\n" cat bar 87 atf_check_not_equal "$(stat -f%d,%i foo)" "$(stat -f%d,%i bar)" 88} 89 90atf_test_case hardlink_exists_force 91hardlink_exists_force_body() 92{ 93 echo "foo" >foo 94 echo "bar" >bar 95 atf_check cp -fl foo bar 96 atf_check -o inline:"foo\n" cat bar 97 atf_check_equal "$(stat -f%d,%i foo)" "$(stat -f%d,%i bar)" 98} 99 100atf_test_case matching_srctgt 101matching_srctgt_body() 102{ 103 104 # PR235438: `cp -R foo foo` would previously infinitely recurse and 105 # eventually error out. 106 mkdir foo 107 echo "qux" > foo/bar 108 cp foo/bar foo/zoo 109 110 atf_check cp -R foo foo 111 atf_check -o inline:"qux\n" cat foo/foo/bar 112 atf_check -o inline:"qux\n" cat foo/foo/zoo 113 atf_check -e not-empty -s not-exit:0 stat foo/foo/foo 114} 115 116atf_test_case matching_srctgt_contained 117matching_srctgt_contained_body() 118{ 119 120 # Let's do the same thing, except we'll try to recursively copy foo into 121 # one of its subdirectories. 122 mkdir foo 123 ln -s foo coo 124 echo "qux" > foo/bar 125 mkdir foo/moo 126 touch foo/moo/roo 127 cp foo/bar foo/zoo 128 129 atf_check cp -R foo foo/moo 130 atf_check cp -RH coo foo/moo 131 atf_check -o inline:"qux\n" cat foo/moo/foo/bar 132 atf_check -o inline:"qux\n" cat foo/moo/coo/bar 133 atf_check -o inline:"qux\n" cat foo/moo/foo/zoo 134 atf_check -o inline:"qux\n" cat foo/moo/coo/zoo 135 136 # We should have copied the contents of foo/moo before foo, coo started 137 # getting copied in. 138 atf_check -o not-empty stat foo/moo/foo/moo/roo 139 atf_check -o not-empty stat foo/moo/coo/moo/roo 140 atf_check -e not-empty -s not-exit:0 stat foo/moo/foo/moo/foo 141 atf_check -e not-empty -s not-exit:0 stat foo/moo/coo/moo/coo 142} 143 144atf_test_case matching_srctgt_link 145matching_srctgt_link_body() 146{ 147 148 mkdir foo 149 echo "qux" > foo/bar 150 cp foo/bar foo/zoo 151 152 atf_check ln -s foo roo 153 atf_check cp -RH roo foo 154 atf_check -o inline:"qux\n" cat foo/roo/bar 155 atf_check -o inline:"qux\n" cat foo/roo/zoo 156} 157 158atf_test_case matching_srctgt_nonexistent 159matching_srctgt_nonexistent_body() 160{ 161 162 # We'll copy foo to a nonexistent subdirectory; ideally, we would 163 # skip just the directory and end up with a layout like; 164 # 165 # foo/ 166 # bar 167 # dne/ 168 # bar 169 # zoo 170 # zoo 171 # 172 mkdir foo 173 echo "qux" > foo/bar 174 cp foo/bar foo/zoo 175 176 atf_check cp -R foo foo/dne 177 atf_check -o inline:"qux\n" cat foo/dne/bar 178 atf_check -o inline:"qux\n" cat foo/dne/zoo 179 atf_check -e not-empty -s not-exit:0 stat foo/dne/foo 180} 181 182recursive_link_setup() 183{ 184 extra_cpflag=$1 185 186 mkdir -p foo/bar 187 ln -s bar foo/baz 188 189 mkdir foo-mirror 190 eval "cp -R $extra_cpflag foo foo-mirror" 191} 192 193atf_test_case recursive_link_dflt 194recursive_link_dflt_body() 195{ 196 recursive_link_setup 197 198 # -P is the default, so this should work and preserve the link. 199 atf_check cp -R foo foo-mirror 200 atf_check test -L foo-mirror/foo/baz 201} 202 203atf_test_case recursive_link_Hflag 204recursive_link_Hflag_body() 205{ 206 recursive_link_setup 207 208 # -H will not follow either, so this should also work and preserve the 209 # link. 210 atf_check cp -RH foo foo-mirror 211 atf_check test -L foo-mirror/foo/baz 212} 213 214atf_test_case recursive_link_Lflag 215recursive_link_Lflag_body() 216{ 217 recursive_link_setup -L 218 219 # -L will work, but foo/baz ends up expanded to a directory. 220 atf_check test -d foo-mirror/foo/baz -a \ 221 '(' ! -L foo-mirror/foo/baz ')' 222 atf_check cp -RL foo foo-mirror 223 atf_check test -d foo-mirror/foo/baz -a \ 224 '(' ! -L foo-mirror/foo/baz ')' 225} 226 227atf_test_case samefile 228samefile_body() 229{ 230 echo "foo" >foo 231 ln foo bar 232 ln -s bar baz 233 atf_check -e match:"baz and baz are identical" \ 234 -s exit:1 cp baz baz 235 atf_check -e match:"bar and baz are identical" \ 236 -s exit:1 cp baz bar 237 atf_check -e match:"foo and baz are identical" \ 238 -s exit:1 cp baz foo 239 atf_check -e match:"bar and foo are identical" \ 240 -s exit:1 cp foo bar 241} 242 243file_is_sparse() 244{ 245 atf_check ${0%/*}/sparse "$1" 246} 247 248files_are_equal() 249{ 250 atf_check_not_equal "$(stat -f%d,%i "$1")" "$(stat -f%d,%i "$2")" 251 atf_check cmp "$1" "$2" 252} 253 254atf_test_case sparse_leading_hole 255sparse_leading_hole_body() 256{ 257 # A 16-megabyte hole followed by one megabyte of data 258 truncate -s 16M foo 259 seq -f%015g 65536 >>foo 260 file_is_sparse foo 261 262 atf_check cp foo bar 263 files_are_equal foo bar 264 file_is_sparse bar 265} 266 267atf_test_case sparse_multiple_holes 268sparse_multiple_holes_body() 269{ 270 # Three one-megabyte blocks of data preceded, separated, and 271 # followed by 16-megabyte holes 272 truncate -s 16M foo 273 seq -f%015g 65536 >>foo 274 truncate -s 33M foo 275 seq -f%015g 65536 >>foo 276 truncate -s 50M foo 277 seq -f%015g 65536 >>foo 278 truncate -s 67M foo 279 file_is_sparse foo 280 281 atf_check cp foo bar 282 files_are_equal foo bar 283 file_is_sparse bar 284} 285 286atf_test_case sparse_only_hole 287sparse_only_hole_body() 288{ 289 # A 16-megabyte hole 290 truncate -s 16M foo 291 file_is_sparse foo 292 293 atf_check cp foo bar 294 files_are_equal foo bar 295 file_is_sparse bar 296} 297 298atf_test_case sparse_to_dev 299sparse_to_dev_body() 300{ 301 # Three one-megabyte blocks of data preceded, separated, and 302 # followed by 16-megabyte holes 303 truncate -s 16M foo 304 seq -f%015g 65536 >>foo 305 truncate -s 33M foo 306 seq -f%015g 65536 >>foo 307 truncate -s 50M foo 308 seq -f%015g 65536 >>foo 309 truncate -s 67M foo 310 file_is_sparse foo 311 312 atf_check -o file:foo cp foo /dev/stdout 313} 314 315atf_test_case sparse_trailing_hole 316sparse_trailing_hole_body() 317{ 318 # One megabyte of data followed by a 16-megabyte hole 319 seq -f%015g 65536 >foo 320 truncate -s 17M foo 321 file_is_sparse foo 322 323 atf_check cp foo bar 324 files_are_equal foo bar 325 file_is_sparse bar 326} 327 328atf_test_case standalone_Pflag 329standalone_Pflag_body() 330{ 331 echo "foo" > bar 332 ln -s bar foo 333 334 atf_check cp -P foo baz 335 atf_check -o inline:'Symbolic Link\n' stat -f %SHT baz 336} 337 338atf_test_case symlink 339symlink_body() 340{ 341 echo "foo" >foo 342 atf_check cp -s foo bar 343 atf_check -o inline:"foo\n" cat bar 344 atf_check -o inline:"foo\n" readlink bar 345} 346 347atf_test_case symlink_exists 348symlink_exists_body() 349{ 350 echo "foo" >foo 351 echo "bar" >bar 352 atf_check -s not-exit:0 -e match:exists cp -s foo bar 353 atf_check -o inline:"bar\n" cat bar 354} 355 356atf_test_case symlink_exists_force 357symlink_exists_force_body() 358{ 359 echo "foo" >foo 360 echo "bar" >bar 361 atf_check cp -fs foo bar 362 atf_check -o inline:"foo\n" cat bar 363 atf_check -o inline:"foo\n" readlink bar 364} 365 366atf_init_test_cases() 367{ 368 atf_add_test_case basic 369 atf_add_test_case basic_symlink 370 atf_add_test_case chrdev 371 atf_add_test_case hardlink 372 atf_add_test_case hardlink_exists 373 atf_add_test_case hardlink_exists_force 374 atf_add_test_case matching_srctgt 375 atf_add_test_case matching_srctgt_contained 376 atf_add_test_case matching_srctgt_link 377 atf_add_test_case matching_srctgt_nonexistent 378 atf_add_test_case recursive_link_dflt 379 atf_add_test_case recursive_link_Hflag 380 atf_add_test_case recursive_link_Lflag 381 atf_add_test_case samefile 382 atf_add_test_case sparse_leading_hole 383 atf_add_test_case sparse_multiple_holes 384 atf_add_test_case sparse_only_hole 385 atf_add_test_case sparse_to_dev 386 atf_add_test_case sparse_trailing_hole 387 atf_add_test_case standalone_Pflag 388 atf_add_test_case symlink 389 atf_add_test_case symlink_exists 390 atf_add_test_case symlink_exists_force 391} 392