xref: /freebsd/bin/cp/tests/cp_test.sh (revision 9f23cbd6cae82fd77edfad7173432fa8dccd0a95)
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# $FreeBSD$
28
29check_size()
30{
31	file=$1
32	sz=$2
33
34	atf_check -o inline:"$sz\n" stat -f '%z' $file
35}
36
37atf_test_case basic
38basic_body()
39{
40	echo "foo" > bar
41
42	atf_check cp bar baz
43	check_size baz 4
44}
45
46atf_test_case basic_symlink
47basic_symlink_body()
48{
49	echo "foo" > bar
50	ln -s bar baz
51
52	atf_check cp baz foo
53	atf_check test '!' -L foo
54
55	atf_check -e inline:"cp: baz and baz are identical (not copied).\n" \
56	    -s exit:1 cp baz baz
57	atf_check -e inline:"cp: bar and baz are identical (not copied).\n" \
58	    -s exit:1 cp baz bar
59}
60
61atf_test_case chrdev
62chrdev_body()
63{
64	echo "foo" > bar
65
66	check_size bar 4
67	atf_check cp /dev/null trunc
68	check_size trunc 0
69	atf_check cp bar trunc
70	check_size trunc 4
71	atf_check cp /dev/null trunc
72	check_size trunc 0
73}
74
75atf_test_case matching_srctgt
76matching_srctgt_body()
77{
78
79	# PR235438: `cp -R foo foo` would previously infinitely recurse and
80	# eventually error out.
81	mkdir foo
82	echo "qux" > foo/bar
83	cp foo/bar foo/zoo
84
85	atf_check cp -R foo foo
86	atf_check -o inline:"qux\n" cat foo/foo/bar
87	atf_check -o inline:"qux\n" cat foo/foo/zoo
88	atf_check -e not-empty -s not-exit:0 stat foo/foo/foo
89}
90
91atf_test_case matching_srctgt_contained
92matching_srctgt_contained_body()
93{
94
95	# Let's do the same thing, except we'll try to recursively copy foo into
96	# one of its subdirectories.
97	mkdir foo
98	ln -s foo coo
99	echo "qux" > foo/bar
100	mkdir foo/moo
101	touch foo/moo/roo
102	cp foo/bar foo/zoo
103
104	atf_check cp -R foo foo/moo
105	atf_check cp -RH coo foo/moo
106	atf_check -o inline:"qux\n" cat foo/moo/foo/bar
107	atf_check -o inline:"qux\n" cat foo/moo/coo/bar
108	atf_check -o inline:"qux\n" cat foo/moo/foo/zoo
109	atf_check -o inline:"qux\n" cat foo/moo/coo/zoo
110
111	# We should have copied the contents of foo/moo before foo, coo started
112	# getting copied in.
113	atf_check -o not-empty stat foo/moo/foo/moo/roo
114	atf_check -o not-empty stat foo/moo/coo/moo/roo
115	atf_check -e not-empty -s not-exit:0 stat foo/moo/foo/moo/foo
116	atf_check -e not-empty -s not-exit:0 stat foo/moo/coo/moo/coo
117}
118
119atf_test_case matching_srctgt_link
120matching_srctgt_link_body()
121{
122
123	mkdir foo
124	echo "qux" > foo/bar
125	cp foo/bar foo/zoo
126
127	atf_check ln -s foo roo
128	atf_check cp -RH roo foo
129	atf_check -o inline:"qux\n" cat foo/roo/bar
130	atf_check -o inline:"qux\n" cat foo/roo/zoo
131}
132
133atf_test_case matching_srctgt_nonexistent
134matching_srctgt_nonexistent_body()
135{
136
137	# We'll copy foo to a nonexistent subdirectory; ideally, we would
138	# skip just the directory and end up with a layout like;
139	#
140	# foo/
141	#     bar
142	#     dne/
143	#         bar
144	#         zoo
145	#     zoo
146	#
147	mkdir foo
148	echo "qux" > foo/bar
149	cp foo/bar foo/zoo
150
151	atf_check cp -R foo foo/dne
152	atf_check -o inline:"qux\n" cat foo/dne/bar
153	atf_check -o inline:"qux\n" cat foo/dne/zoo
154	atf_check -e not-empty -s not-exit:0 stat foo/dne/foo
155}
156
157recursive_link_setup()
158{
159	extra_cpflag=$1
160
161	mkdir -p foo/bar
162	ln -s bar foo/baz
163
164	mkdir foo-mirror
165	eval "cp -R $extra_cpflag foo foo-mirror"
166}
167
168atf_test_case recursive_link_dflt
169recursive_link_dflt_body()
170{
171	recursive_link_setup
172
173	# -P is the default, so this should work and preserve the link.
174	atf_check cp -R foo foo-mirror
175	atf_check test -L foo-mirror/foo/baz
176}
177
178atf_test_case recursive_link_Hflag
179recursive_link_Hflag_body()
180{
181	recursive_link_setup
182
183	# -H will not follow either, so this should also work and preserve the
184	# link.
185	atf_check cp -RH foo foo-mirror
186	atf_check test -L foo-mirror/foo/baz
187}
188
189atf_test_case recursive_link_Lflag
190recursive_link_Lflag_body()
191{
192	recursive_link_setup -L
193
194	# -L will work, but foo/baz ends up expanded to a directory.
195	atf_check test -d foo-mirror/foo/baz -a \
196	    '(' ! -L foo-mirror/foo/baz ')'
197	atf_check cp -RL foo foo-mirror
198	atf_check test -d foo-mirror/foo/baz -a \
199	    '(' ! -L foo-mirror/foo/baz ')'
200}
201
202file_is_sparse()
203{
204	atf_check ${0%/*}/sparse "$1"
205}
206
207files_are_equal()
208{
209	atf_check test "$(stat -f "%d %i" "$1")" != "$(stat -f "%d %i" "$2")"
210	atf_check cmp "$1" "$2"
211}
212
213atf_test_case sparse_leading_hole
214sparse_leading_hole_body()
215{
216	# A 16-megabyte hole followed by one megabyte of data
217	truncate -s 16M foo
218	seq -f%015g 65536 >>foo
219	file_is_sparse foo
220
221	atf_check cp foo bar
222	files_are_equal foo bar
223	file_is_sparse bar
224}
225
226atf_test_case sparse_multiple_holes
227sparse_multiple_holes_body()
228{
229	# Three one-megabyte blocks of data preceded, separated, and
230	# followed by 16-megabyte holes
231	truncate -s 16M foo
232	seq -f%015g 65536 >>foo
233	truncate -s 33M foo
234	seq -f%015g 65536 >>foo
235	truncate -s 50M foo
236	seq -f%015g 65536 >>foo
237	truncate -s 67M foo
238	file_is_sparse foo
239
240	atf_check cp foo bar
241	files_are_equal foo bar
242	file_is_sparse bar
243}
244
245atf_test_case sparse_only_hole
246sparse_only_hole_body()
247{
248	# A 16-megabyte hole
249	truncate -s 16M foo
250	file_is_sparse foo
251
252	atf_check cp foo bar
253	files_are_equal foo bar
254	file_is_sparse bar
255}
256
257atf_test_case sparse_to_dev
258sparse_to_dev_body()
259{
260	# Three one-megabyte blocks of data preceded, separated, and
261	# followed by 16-megabyte holes
262	truncate -s 16M foo
263	seq -f%015g 65536 >>foo
264	truncate -s 33M foo
265	seq -f%015g 65536 >>foo
266	truncate -s 50M foo
267	seq -f%015g 65536 >>foo
268	truncate -s 67M foo
269	file_is_sparse foo
270
271	atf_check -o file:foo cp foo /dev/stdout
272}
273
274atf_test_case sparse_trailing_hole
275sparse_trailing_hole_body()
276{
277	# One megabyte of data followed by a 16-megabyte hole
278	seq -f%015g 65536 >foo
279	truncate -s 17M foo
280	file_is_sparse foo
281
282	atf_check cp foo bar
283	files_are_equal foo bar
284	file_is_sparse bar
285}
286
287atf_test_case standalone_Pflag
288standalone_Pflag_body()
289{
290	echo "foo" > bar
291	ln -s bar foo
292
293	atf_check cp -P foo baz
294	atf_check -o inline:'Symbolic Link\n' stat -f %SHT baz
295}
296
297atf_init_test_cases()
298{
299	atf_add_test_case basic
300	atf_add_test_case basic_symlink
301	atf_add_test_case chrdev
302	atf_add_test_case matching_srctgt
303	atf_add_test_case matching_srctgt_contained
304	atf_add_test_case matching_srctgt_link
305	atf_add_test_case matching_srctgt_nonexistent
306	atf_add_test_case recursive_link_dflt
307	atf_add_test_case recursive_link_Hflag
308	atf_add_test_case recursive_link_Lflag
309	atf_add_test_case sparse_leading_hole
310	atf_add_test_case sparse_multiple_holes
311	atf_add_test_case sparse_only_hole
312	atf_add_test_case sparse_to_dev
313	atf_add_test_case sparse_trailing_hole
314	atf_add_test_case standalone_Pflag
315}
316