xref: /illumos-gate/usr/src/test/util-tests/tests/cpmvln/no-targ.ksh (revision c75092296cd7a466f779e1785caee762bf71f3bc)
1*c7509229SRobert Mustacchi#!/usr/bin/ksh
2*c7509229SRobert Mustacchi#
3*c7509229SRobert Mustacchi# This file and its contents are supplied under the terms of the
4*c7509229SRobert Mustacchi# Common Development and Distribution License ("CDDL"), version 1.0.
5*c7509229SRobert Mustacchi# You may only use this file in accordance with the terms of version
6*c7509229SRobert Mustacchi# 1.0 of the CDDL.
7*c7509229SRobert Mustacchi#
8*c7509229SRobert Mustacchi# A full copy of the text of the CDDL should have accompanied this
9*c7509229SRobert Mustacchi# source.  A copy of the CDDL is also available via the Internet at
10*c7509229SRobert Mustacchi# http://www.illumos.org/license/CDDL.
11*c7509229SRobert Mustacchi#
12*c7509229SRobert Mustacchi
13*c7509229SRobert Mustacchi#
14*c7509229SRobert Mustacchi# Copyright 2026 Oxide Computer Company
15*c7509229SRobert Mustacchi#
16*c7509229SRobert Mustacchi
17*c7509229SRobert Mustacchi#
18*c7509229SRobert Mustacchi# Test the various flavors of cp, mv, and ln with the -T flag. The -T flag
19*c7509229SRobert Mustacchi# briefly requires that there are always two operands and says to treat the
20*c7509229SRobert Mustacchi# destination like a file with respect to removing and everything else.
21*c7509229SRobert Mustacchi#
22*c7509229SRobert Mustacchi
23*c7509229SRobert Mustacchiunalias -a
24*c7509229SRobert Mustacchiset -o pipefail
25*c7509229SRobert Mustacchiexport LANG=C.UTF-8
26*c7509229SRobert Mustacchi
27*c7509229SRobert Mustacchi#
28*c7509229SRobert Mustacchi# Program paths for interposing while testing.
29*c7509229SRobert Mustacchi#
30*c7509229SRobert MustacchiMV=${MV:-"/usr/bin/mv"}
31*c7509229SRobert MustacchiXMV=${XMV:-"/usr/xpg4/bin/mv"}
32*c7509229SRobert MustacchiCP=${CP:-"/usr/bin/cp"}
33*c7509229SRobert MustacchiXCP=${XCP:-"/usr/xpg4/bin/cp"}
34*c7509229SRobert MustacchiLN=${LN:-"/usr/bin/ln"}
35*c7509229SRobert MustacchiXLN=${XLN:-"/usr/xpg4/bin/ln"}
36*c7509229SRobert Mustacchi
37*c7509229SRobert Mustacchi
38*c7509229SRobert Mustacchint_exit=0
39*c7509229SRobert Mustacchint_arg0=$(basename $0)
40*c7509229SRobert Mustacchint_work="/tmp/$nt_arg0.$$"
41*c7509229SRobert Mustacchi
42*c7509229SRobert Mustacchifunction fatal
43*c7509229SRobert Mustacchi{
44*c7509229SRobert Mustacchi	typeset msg="$*"
45*c7509229SRobert Mustacchi	echo "TEST FAILED: $msg" >&2
46*c7509229SRobert Mustacchi	exit 1
47*c7509229SRobert Mustacchi}
48*c7509229SRobert Mustacchi
49*c7509229SRobert Mustacchifunction warn
50*c7509229SRobert Mustacchi{
51*c7509229SRobert Mustacchi	typeset msg="$*"
52*c7509229SRobert Mustacchi	echo "TEST FAILED: $msg" >&2
53*c7509229SRobert Mustacchi	nt_exit=1
54*c7509229SRobert Mustacchi}
55*c7509229SRobert Mustacchi
56*c7509229SRobert Mustacchifunction cleanup
57*c7509229SRobert Mustacchi{
58*c7509229SRobert Mustacchi	rm -rf $nt_work/
59*c7509229SRobert Mustacchi}
60*c7509229SRobert Mustacchi
61*c7509229SRobert Mustacchifunction setup
62*c7509229SRobert Mustacchi{
63*c7509229SRobert Mustacchi	typeset f
64*c7509229SRobert Mustacchi	mkdir "$nt_work" || fatal "failed to make test directory"
65*c7509229SRobert Mustacchi	for f in file1 file2 file3; do
66*c7509229SRobert Mustacchi		echo $f > "$nt_work/$f" || fatal "failed to create $f"
67*c7509229SRobert Mustacchi	done
68*c7509229SRobert Mustacchi
69*c7509229SRobert Mustacchi	mkdir -p "$nt_work/dir1/foo/bar" || fatal "failed to create $f"
70*c7509229SRobert Mustacchi	echo nested > "$nt_work/dir1/foo/bar/nested" || fatal \
71*c7509229SRobert Mustacchi	    "failed to create nested file"
72*c7509229SRobert Mustacchi	mkdir -p "$nt_work/empty" || fatal "failed to create $f"
73*c7509229SRobert Mustacchi}
74*c7509229SRobert Mustacchi
75*c7509229SRobert Mustacchifunction exp_fail
76*c7509229SRobert Mustacchi{
77*c7509229SRobert Mustacchi	typeset desc="$1"
78*c7509229SRobert Mustacchi	shift
79*c7509229SRobert Mustacchi
80*c7509229SRobert Mustacchi	if $* 2>/dev/null; then
81*c7509229SRobert Mustacchi		warn "$desc unexpectedly worked"
82*c7509229SRobert Mustacchi		return
83*c7509229SRobert Mustacchi	fi
84*c7509229SRobert Mustacchi
85*c7509229SRobert Mustacchi	printf "TEST PASSED: %s correctly failed\n" "$desc"
86*c7509229SRobert Mustacchi}
87*c7509229SRobert Mustacchi
88*c7509229SRobert Mustacchifunction check_one
89*c7509229SRobert Mustacchi{
90*c7509229SRobert Mustacchi	typeset desc="$1"
91*c7509229SRobert Mustacchi	typeset check="$2"
92*c7509229SRobert Mustacchi	typeset data="$3"
93*c7509229SRobert Mustacchi	typeset comp=$(<$check)
94*c7509229SRobert Mustacchi
95*c7509229SRobert Mustacchi	if [[ "$comp" != "$data" ]]; then
96*c7509229SRobert Mustacchi		warn "$desc: $check didn't have expected data: " \
97*c7509229SRobert Mustacchi		    "found: '$comp', expected: '$data'"
98*c7509229SRobert Mustacchi		return
99*c7509229SRobert Mustacchi	fi
100*c7509229SRobert Mustacchi
101*c7509229SRobert Mustacchi	printf "TEST PASSED: %s: found expected data\n" "$desc"
102*c7509229SRobert Mustacchi}
103*c7509229SRobert Mustacchi
104*c7509229SRobert Mustacchi#
105*c7509229SRobert Mustacchi# Run a command that relates to a file. Check that the target file has the
106*c7509229SRobert Mustacchi# expected contents in the end.
107*c7509229SRobert Mustacchi#
108*c7509229SRobert Mustacchifunction exp_pass
109*c7509229SRobert Mustacchi{
110*c7509229SRobert Mustacchi	typeset desc="$1"
111*c7509229SRobert Mustacchi	typeset check="$2"
112*c7509229SRobert Mustacchi	typeset data="$3"
113*c7509229SRobert Mustacchi	typeset comp=
114*c7509229SRobert Mustacchi
115*c7509229SRobert Mustacchi	shift; shift; shift
116*c7509229SRobert Mustacchi	if ! $*; then
117*c7509229SRobert Mustacchi		warn "$desc: failed to run $*"
118*c7509229SRobert Mustacchi		return
119*c7509229SRobert Mustacchi	fi
120*c7509229SRobert Mustacchi
121*c7509229SRobert Mustacchi	check_one "$desc" "$check" "$data"
122*c7509229SRobert Mustacchi}
123*c7509229SRobert Mustacchi
124*c7509229SRobert Mustacchitrap cleanup EXIT
125*c7509229SRobert Mustacchi
126*c7509229SRobert Mustacchi#
127*c7509229SRobert Mustacchi# First go through and verify cases where we have only one argument or where we
128*c7509229SRobert Mustacchi# have more than one argument. Normally only ln works with only a single file.
129*c7509229SRobert Mustacchi#
130*c7509229SRobert Mustacchisetup
131*c7509229SRobert Mustacchifor f in "$LN" "$XLN" "$LN -s" "$XLN -s" "$MV" "$XMV" "$CP" "$XCP"; do
132*c7509229SRobert Mustacchi	exp_fail "$f -T one arg" $f -T "$nt_work/file1"
133*c7509229SRobert Mustacchi	exp_fail "$f -T three args" $f -T "$nt_work/file1" "$nt_work/file2" \
134*c7509229SRobert Mustacchi	    "$nt_work/dir1"
135*c7509229SRobert Mustacchidone
136*c7509229SRobert Mustacchi
137*c7509229SRobert Mustacchi#
138*c7509229SRobert Mustacchi# First go through and make sure that things do what we expect with basic file
139*c7509229SRobert Mustacchi# to file movement without -T. We clean before every test to make sure that we
140*c7509229SRobert Mustacchi# have a pristine environment. We also do an override pass here using -f to make
141*c7509229SRobert Mustacchi# sure that we can clobber as expected.
142*c7509229SRobert Mustacchi#
143*c7509229SRobert Mustacchifor f in "$LN" "$XLN" "$LN -s" "$XLN -s" "$MV" "$XMV" "$CP" "$XCP"; do
144*c7509229SRobert Mustacchi	cleanup; setup
145*c7509229SRobert Mustacchi	exp_pass "$f file to file" "$nt_work/targ" "file1" $f "$nt_work/file1" \
146*c7509229SRobert Mustacchi	    "$nt_work/targ"
147*c7509229SRobert Mustacchi	exp_pass "$f -f file to file" "$nt_work/file3" "file2" $f -f \
148*c7509229SRobert Mustacchi	    "$nt_work/file2" "$nt_work/file3"
149*c7509229SRobert Mustacchidone
150*c7509229SRobert Mustacchi
151*c7509229SRobert Mustacchi#
152*c7509229SRobert Mustacchi# Now do the same, but moving contents into directories. Both with and without
153*c7509229SRobert Mustacchi# -n. -n shouldn't care because the target doesn't exist.
154*c7509229SRobert Mustacchi#
155*c7509229SRobert Mustacchifor f in "$LN" "$XLN" "$LN -s" "$XLN -s" "$MV" "$XMV" "$CP" "$XCP"; do
156*c7509229SRobert Mustacchi	cleanup; setup
157*c7509229SRobert Mustacchi	exp_pass "$f file to dir" "$nt_work/dir1/file1" "file1" $f \
158*c7509229SRobert Mustacchi	    "$nt_work/file1" "$nt_work/dir1"
159*c7509229SRobert Mustacchi	cleanup; setup
160*c7509229SRobert Mustacchi	exp_pass "$f -n file to dir" "$nt_work/dir1/file1" "file1" $f -n \
161*c7509229SRobert Mustacchi	    "$nt_work/file1" "$nt_work/dir1"
162*c7509229SRobert Mustacchidone
163*c7509229SRobert Mustacchi
164*c7509229SRobert Mustacchi#
165*c7509229SRobert Mustacchi# Now use -T for file to file, which basically should be the same as before for
166*c7509229SRobert Mustacchi# both overwrite or not.
167*c7509229SRobert Mustacchi#
168*c7509229SRobert Mustacchifor f in "$LN" "$XLN" "$LN -s" "$XLN -s" "$MV" "$XMV" "$CP" "$XCP"; do
169*c7509229SRobert Mustacchi	cleanup; setup
170*c7509229SRobert Mustacchi	exp_pass "$f -T file to file" "$nt_work/targ" "file1" $f -T \
171*c7509229SRobert Mustacchi	    "$nt_work/file1" "$nt_work/targ"
172*c7509229SRobert Mustacchi	exp_pass "$f -Tf file to file" "$nt_work/file3" "file2" $f -Tf \
173*c7509229SRobert Mustacchi	    "$nt_work/file2" "$nt_work/file3"
174*c7509229SRobert Mustacchidone
175*c7509229SRobert Mustacchi
176*c7509229SRobert Mustacchi#
177*c7509229SRobert Mustacchi# Verify that -n on its own is honored correctly for file to file. Then do the
178*c7509229SRobert Mustacchi# same with -T. The tools are inconsistent about exiting zero or not, so we
179*c7509229SRobert Mustacchi# check file contents instead.
180*c7509229SRobert Mustacchi#
181*c7509229SRobert Mustacchifor f in "$LN" "$XLN" "$LN -s" "$XLN -s" "$MV" "$XMV" "$CP" "$XCP"; do
182*c7509229SRobert Mustacchi	cleanup; setup
183*c7509229SRobert Mustacchi	$f -n "$nt_work/file1" "$nt_work/file2" >/dev/null 2>/dev/null
184*c7509229SRobert Mustacchi	check_one "$f file to file -n source intact" "$nt_work/file1" "file1"
185*c7509229SRobert Mustacchi	check_one "$f file to file -n dest intact" "$nt_work/file2" "file2"
186*c7509229SRobert Mustacchi	$f -Tn "$nt_work/file1" "$nt_work/file2" >/dev/null 2>/dev/null
187*c7509229SRobert Mustacchi	check_one "$f -Tn file to file source intact" "$nt_work/file1" "file1"
188*c7509229SRobert Mustacchi	check_one "$f -Tn file to file dest intact" "$nt_work/file2" "file2"
189*c7509229SRobert Mustacchidone
190*c7509229SRobert Mustacchi
191*c7509229SRobert Mustacchi#
192*c7509229SRobert Mustacchi# Now one of the major differences with -T. If we use -Tn with the target of a
193*c7509229SRobert Mustacchi# directory, nothing should happen, there shouldn't be a file inside of the
194*c7509229SRobert Mustacchi# directory, and our original file should still exist.
195*c7509229SRobert Mustacchi#
196*c7509229SRobert Mustacchifor f in "$LN" "$XLN" "$LN -s" "$XLN -s" "$MV" "$XMV" "$CP" "$XCP"; do
197*c7509229SRobert Mustacchi	cleanup; setup
198*c7509229SRobert Mustacchi	$f -Tn "$nt_work/file1" "$nt_work/dir1" >/dev/null 2>/dev/null
199*c7509229SRobert Mustacchi	check_one "$f -Tn file to dir source intact" "$nt_work/file1" "file1"
200*c7509229SRobert Mustacchi	if [[ ! -d "$nt_work/dir1" ]]; then
201*c7509229SRobert Mustacchi		warn "$f -Tn file to dir incorrectly removed dir"
202*c7509229SRobert Mustacchi	else
203*c7509229SRobert Mustacchi		printf "TEST PASSED: %s -Tn file to dir dir intact\n" "$f"
204*c7509229SRobert Mustacchi	fi
205*c7509229SRobert Mustacchi
206*c7509229SRobert Mustacchi	if [[ -e "$nt_work/dir1/file1" ]]; then
207*c7509229SRobert Mustacchi		warn "$f -Tn file to dir incorrectly created file"
208*c7509229SRobert Mustacchi	else
209*c7509229SRobert Mustacchi		printf "TEST PASSED: %s -Tn didn't put file in dir\n" "$f"
210*c7509229SRobert Mustacchi	fi
211*c7509229SRobert Mustacchidone
212*c7509229SRobert Mustacchi
213*c7509229SRobert Mustacchi#
214*c7509229SRobert Mustacchi# Verify that if our target is an empty directory, we don't overwrite it when
215*c7509229SRobert Mustacchi# using -T. This should be true regardless of -f.
216*c7509229SRobert Mustacchi#
217*c7509229SRobert Mustacchifor f in "$LN" "$XLN" "$LN -s" "$XLN -s" "$MV" "$XMV" "$CP" "$XCP"; do
218*c7509229SRobert Mustacchi	cleanup; setup
219*c7509229SRobert Mustacchi	$f -Tf "$nt_work/file1" "$nt_work/empty" 2>/dev/null
220*c7509229SRobert Mustacchi	check_one "$f -Tf file to dir source intact" "$nt_work/file1" "file1"
221*c7509229SRobert Mustacchi	if [[ ! -d "$nt_work/empty" ]]; then
222*c7509229SRobert Mustacchi		warn "$f -Tf file to dir incorrectly removed dir"
223*c7509229SRobert Mustacchi	else
224*c7509229SRobert Mustacchi		printf "TEST PASSED: %s -Tf file to dir dir intact\n" "$f"
225*c7509229SRobert Mustacchi	fi
226*c7509229SRobert Mustacchi
227*c7509229SRobert Mustacchi	if [[ -e "$nt_work/empty/file1" ]]; then
228*c7509229SRobert Mustacchi		warn "$f -Tn file to dir incorrectly created file"
229*c7509229SRobert Mustacchi	else
230*c7509229SRobert Mustacchi		printf "TEST PASSED: %s -Tf didn't put file in dir\n" "$f"
231*c7509229SRobert Mustacchi	fi
232*c7509229SRobert Mustacchidone
233*c7509229SRobert Mustacchi
234*c7509229SRobert Mustacchi#
235*c7509229SRobert Mustacchi# Now that we have done various versions of going file->* we need to turn around
236*c7509229SRobert Mustacchi# and do dir->*. We start with the base case of a directory to a file. The
237*c7509229SRobert Mustacchi# behavior here is tool specific:
238*c7509229SRobert Mustacchi#
239*c7509229SRobert Mustacchi#  - mv does not allow this.
240*c7509229SRobert Mustacchi#  - cp does not allow this.
241*c7509229SRobert Mustacchi#  - We don't want to generate directory hardlinks with ln
242*c7509229SRobert Mustacchi#  - ln without -f will refuse because it exists. However, ln with -f will
243*c7509229SRobert Mustacchi#    normally touch it. We focus on the non -f case so we have a consistent
244*c7509229SRobert Mustacchi#    failure.
245*c7509229SRobert Mustacchi#
246*c7509229SRobert Mustacchicleanup; setup
247*c7509229SRobert Mustacchifor f in "$LN -s" "$XLN -s" "$MV" "$XMV" "$CP" "$XCP"; do
248*c7509229SRobert Mustacchi	exp_fail "$f dir to file" $f "$nt_work/empty" "$nt_work/file1"
249*c7509229SRobert Mustacchidone
250*c7509229SRobert Mustacchi
251*c7509229SRobert Mustacchi#
252*c7509229SRobert Mustacchi# Now directory to directory. In the case of non-T operation, everything should
253*c7509229SRobert Mustacchi# happily work and create it inside of the target directory. However, with -T
254*c7509229SRobert Mustacchi# the behavior is different:
255*c7509229SRobert Mustacchi#
256*c7509229SRobert Mustacchi#  - mv works if it's an empty target directory, fails otherwise. This is
257*c7509229SRobert Mustacchi#    different from when not using -T.
258*c7509229SRobert Mustacchi#  - cp does not allow this.
259*c7509229SRobert Mustacchi#  - ln -s does not allow it regardless of -f or not.
260*c7509229SRobert Mustacchi#
261*c7509229SRobert Mustacchifor f in "$LN -s" "$XLN -s" "$CP" "$XCP"; do
262*c7509229SRobert Mustacchi	cleanup; setup
263*c7509229SRobert Mustacchi	exp_fail "$f -T dir to dir" $f -T "$nt_work/dir1" "$nt_work/empty"
264*c7509229SRobert Mustacchidone
265*c7509229SRobert Mustacchi
266*c7509229SRobert Mustacchifor f in "$MV" "$XMV"; do
267*c7509229SRobert Mustacchi	cleanup; setup
268*c7509229SRobert Mustacchi	exp_pass "$f -T dir to dir (empty)" "$nt_work/empty/foo/bar/nested" \
269*c7509229SRobert Mustacchi	    "nested" $f -T "$nt_work/dir1" "$nt_work/empty"
270*c7509229SRobert Mustacchi	cleanup; setup
271*c7509229SRobert Mustacchi	exp_fail "$f -T dir to dir (non-empty)" $f -T "$nt_work/empty" \
272*c7509229SRobert Mustacchi	    "$nt_work/dir1"
273*c7509229SRobert Mustacchidone
274*c7509229SRobert Mustacchi
275*c7509229SRobert Mustacchi#
276*c7509229SRobert Mustacchi# Finally test a case specific to symlinks: where the source is a directory but
277*c7509229SRobert Mustacchi# the target doesn't exist. Without -T the first time it's created it goes in
278*c7509229SRobert Mustacchi# the top-level dir. The second time because it's a symlink to a directory it
279*c7509229SRobert Mustacchi# goes in the directory. Consider 'ln -s amd64 64' like we use in the build
280*c7509229SRobert Mustacchi# system. The first time it creates 64->amd64 in the top level and then
281*c7509229SRobert Mustacchi# afterwards creates amd64/amd64 which points to itself, which doesn't help
282*c7509229SRobert Mustacchi# anyone. While we are using absolute paths in the test, the same behavior
283*c7509229SRobert Mustacchi# holds.
284*c7509229SRobert Mustacchi#
285*c7509229SRobert Mustacchifor f in "$LN -s" "$XLN -s"; do
286*c7509229SRobert Mustacchi	cleanup; setup
287*c7509229SRobert Mustacchi	ln -s "$nt_work/empty" "$nt_work/64" || fatal "failed to create 64"
288*c7509229SRobert Mustacchi	exp_fail "$f -T existing dir symlink" $f -T "$nt_work/empty" \
289*c7509229SRobert Mustacchi	    "$nt_work/64"
290*c7509229SRobert Mustacchi	if ! $f -Tf "$nt_work/empty" "$nt_work/64"; then
291*c7509229SRobert Mustacchi		warn "$f -Tf existing symlink failed"
292*c7509229SRobert Mustacchi	else
293*c7509229SRobert Mustacchi		printf "TEST PASSED: %s -Tf existing dir symlink failed\n" "$f"
294*c7509229SRobert Mustacchi	fi
295*c7509229SRobert Mustacchidone
296*c7509229SRobert Mustacchi
297*c7509229SRobert Mustacchiif (( nt_exit == 0 )); then
298*c7509229SRobert Mustacchi	printf "All tests passed successfully\n"
299*c7509229SRobert Mustacchifi
300*c7509229SRobert Mustacchiexit $nt_exit
301