xref: /freebsd/bin/chflags/tests/chflags_test.sh (revision a259b98fa211ed87bfee58c575de4e2de94ee0fa)
1#
2# Copyright 2017 Shivansh Rai
3# Copyright (c) 2026 Jitendra Bhati
4# All rights reserved.
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#
28
29usage_output='usage: chflags'
30
31# Skip the calling test if the work filesystem does not support
32# setting the nodump file flag (e.g. some ZFS configurations).
33require_chflags()
34{
35	touch .chflags_probe
36	if ! chflags nodump .chflags_probe 2>/dev/null; then
37		rm -f .chflags_probe
38		atf_skip "filesystem does not support the nodump flag"
39	fi
40	rm -f .chflags_probe
41}
42
43atf_test_case invalid_usage
44invalid_usage_head()
45{
46	atf_set "descr" "Verify that an invalid usage with a supported option produces a valid error message"
47}
48
49invalid_usage_body()
50{
51	atf_check -s not-exit:0 -e match:"$usage_output" chflags -f
52	atf_check -s not-exit:0 -e match:"$usage_output" chflags -H
53	atf_check -s not-exit:0 -e match:"$usage_output" chflags -h
54	atf_check -s not-exit:0 -e match:"$usage_output" chflags -L
55	atf_check -s not-exit:0 -e match:"$usage_output" chflags -P
56	atf_check -s not-exit:0 -e match:"$usage_output" chflags -R
57	atf_check -s not-exit:0 -e match:"$usage_output" chflags -v
58}
59
60atf_test_case no_arguments
61no_arguments_head()
62{
63	atf_set "descr" "Verify that chflags(1) fails and generates a valid usage message when no arguments are supplied"
64}
65
66no_arguments_body()
67{
68	atf_check -s not-exit:0 -e match:"$usage_output" chflags
69}
70
71atf_test_case relative_path
72relative_path_head()
73{
74	atf_set "descr" "chflags sets flags on a relative path"
75}
76relative_path_body()
77{
78	require_chflags
79	touch file
80	atf_check chflags nodump file
81	atf_check -o match:nodump stat -f "%Sf" file
82}
83
84atf_test_case absolute_path
85absolute_path_head()
86{
87	atf_set "descr" "chflags sets flags on an absolute path"
88}
89absolute_path_body()
90{
91	require_chflags
92	touch file
93	atf_check chflags nodump "$(pwd)/file"
94	atf_check -o match:nodump stat -f "%Sf" file
95}
96
97atf_test_case dotdot_path
98dotdot_path_head()
99{
100	atf_set "descr" "chflags sets flags on a path containing .."
101}
102dotdot_path_body()
103{
104	require_chflags
105	mkdir dir
106	touch file
107	cd dir
108	atf_check chflags nodump ../file
109	atf_check -o match:nodump stat -f "%Sf" ../file
110}
111
112atf_test_case recursive
113recursive_head()
114{
115	atf_set "descr" "chflags -R sets flags on a directory tree"
116}
117recursive_body()
118{
119	require_chflags
120	mkdir -p dir/sub
121	touch dir/file dir/sub/file
122	atf_check chflags -R nodump dir
123	atf_check -o match:nodump stat -f "%Sf" dir/file
124	atf_check -o match:nodump stat -f "%Sf" dir/sub/file
125}
126
127atf_test_case mixed_paths
128mixed_paths_head()
129{
130	atf_set "descr" "chflags handles relative, absolute and .. paths together"
131}
132mixed_paths_body()
133{
134	require_chflags
135	mkdir dir
136	touch a dir/b c
137	cd dir
138	atf_check chflags nodump b "$(pwd)/../a" ../c
139	atf_check -o match:nodump stat -f "%Sf" b
140	atf_check -o match:nodump stat -f "%Sf" ../a
141	atf_check -o match:nodump stat -f "%Sf" ../c
142}
143
144atf_test_case outside_symlink_rejected
145outside_symlink_rejected_head()
146{
147	atf_set "descr" "chflags -RL does not follow a symlink pointing " \
148	    "outside the traversal by default"
149}
150outside_symlink_rejected_body()
151{
152	require_chflags
153	mkdir -p foo/bar/baz
154	touch target
155	ln -s ../../../target foo/bar/baz/link
156	atf_check -s not-exit:0 -e ignore chflags -RL nodump foo/bar
157	atf_check -o match:"target[[:space:]]*-" stat -f "%N %Sf" target
158}
159
160atf_test_case outside_symlink_unsafe
161outside_symlink_unsafe_head()
162{
163	atf_set "descr" "chflags -RL --dereference-links-unsafely follows " \
164	    "a symlink pointing outside the traversal"
165}
166outside_symlink_unsafe_body()
167{
168	require_chflags
169	mkdir -p foo/bar/baz
170	touch target
171	ln -s ../../../target foo/bar/baz/link
172	atf_check chflags -RL --dereference-links-unsafely nodump foo/bar
173	atf_check -o match:nodump stat -f "%Sf" target
174}
175
176atf_test_case symlink_no_h
177symlink_no_h_head()
178{
179	atf_set "descr" "chflags on a symlink without -h changes the target"
180}
181symlink_no_h_body()
182{
183	require_chflags
184	mkdir dir
185	touch target
186	ln -s ../target dir/link
187	atf_check chflags nodump dir/link
188	atf_check -o match:nodump stat -f "%Sf" target
189	atf_check -o not-match:nodump stat -f "%Sf" dir/link
190}
191
192atf_test_case symlink_rootlevel_H
193symlink_rootlevel_H_head()
194{
195	atf_set "descr" "chflags -RH follows a symbolic link to a" \
196	    " directory named on the command line"
197}
198symlink_rootlevel_H_body()
199{
200	require_chflags
201	mkdir dir realdir
202	touch realdir/file
203	ln -s ../realdir dir/dlink
204	atf_check chflags -RH nodump dir/dlink
205	atf_check -o match:nodump stat -f "%Sf" realdir/file
206	atf_check -o not-match:nodump stat -f "%Sf" dir/dlink
207}
208
209atf_test_case symlink_rootlevel_H_dead
210symlink_rootlevel_H_dead_head()
211{
212	atf_set "descr" "chflags -RH on a broken symbolic link fails" \
213	    " gracefully"
214}
215symlink_rootlevel_H_dead_body()
216{
217	require_chflags
218	mkdir dir
219	ln -s ../nonexistent dir/dlink
220	atf_check -s not-exit:0 -e match:"No such file or directory" \
221		chflags -RH nodump dir/dlink
222}
223
224atf_init_test_cases()
225{
226	atf_add_test_case invalid_usage
227	atf_add_test_case no_arguments
228	atf_add_test_case relative_path
229	atf_add_test_case absolute_path
230	atf_add_test_case dotdot_path
231	atf_add_test_case recursive
232	atf_add_test_case mixed_paths
233	atf_add_test_case symlink_no_h
234	atf_add_test_case symlink_rootlevel_H
235	atf_add_test_case symlink_rootlevel_H_dead
236	atf_add_test_case outside_symlink_rejected
237	atf_add_test_case outside_symlink_unsafe
238}
239