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