1# 2# Copyright (c) 2017 Dell EMC 3# All rights reserved. 4# Copyright (c) 2025-2026 Klara, Inc. 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: ${CHKPATH:="mnt"} 29: ${NODEV:="#NODEV"} 30 31atf_test_case F_flag 32F_flag_head() 33{ 34 atf_set "descr" "Verify the output format for -F" 35} 36F_flag_body() 37{ 38 # TODO: socket, whiteout file 39 atf_check touch a 40 atf_check mkdir b 41 atf_check install -m 0777 /dev/null c 42 atf_check ln -s a d 43 atf_check mkfifo f 44 45 atf_check -o match:'.* a' stat -Fn a 46 atf_check -o match:'.* b/' stat -Fn b 47 atf_check -o match:'.* c\*' stat -Fn c 48 atf_check -o match:'.* d@' stat -Fn d 49 atf_check -o match:'.* f\|' stat -Fn f 50} 51 52atf_test_case h_flag cleanup 53h_flag_head() 54{ 55 atf_set "descr" "Verify the output format for -h" 56 atf_set "require.user" "root" 57} 58h_flag_body() 59{ 60 file=$(realpath $0) 61 # POSIX defines a hole as “[a] contiguous region of bytes 62 # within a file, all having the value of zero” and requires 63 # that “all seekable files shall have a virtual hole starting 64 # at the current size of the file” but says “it is up to the 65 # implementation to define when sparse files can be created 66 # and with what granularity for the size of holes”. It also 67 # defines a sparse file as “[a] file that contains more holes 68 # than just the virtual hole at the end of the file”. That's 69 # pretty much the extent of its discussion of holes, apart 70 # from the description of SEEK_HOLE and SEEK_DATA in the lseek 71 # manual page. In other words, there is no portable way to 72 # reliably create a hole in a file on any given file system. 73 # 74 # On FreeBSD, this test is likely to run on either tmpfs, ufs 75 # (ffs2), or zfs. Of those three, only tmpfs has predictable 76 # semantics and supports all possible configurations (the 77 # minimum hole size on zfs is variable for small files, and 78 # ufs will not allow a file to end in a hole). 79 atf_check mkdir mnt 80 atf_check mount -t tmpfs tmpfs mnt 81 cd mnt 82 83 # For a directory, prints the minimum hole size, which on 84 # tmpfs is the system page size. 85 ps=$(sysctl -n hw.pagesize) 86 atf_check -o inline:"$((ps)) .\n" stat -h . 87 atf_check -o inline:"$((ps)) ." stat -hn . 88 89 # For a file, prints a list of holes. Some file systems don't 90 # like creating small holes, so we create large ones instead. 91 hs=$((16*1024*1024)) 92 atf_check truncate -s 0 foo 93 atf_check -o inline:"0 foo" \ 94 stat -hn foo 95 atf_check truncate -s "$((hs))" foo 96 atf_check -o inline:"0-$((hs-1)) foo" \ 97 stat -hn foo 98 atf_check dd status=none if="${file}" of=foo \ 99 oseek="$((hs))" bs=1 count=1 100 atf_check -o inline:"0-$((hs-1)),$((hs+1)) foo" \ 101 stat -hn foo 102 atf_check truncate -s "$((hs*3))" foo 103 atf_check -o inline:"0-$((hs-1)),$((hs+ps))-$((hs*3-1)) foo" \ 104 stat -hn foo 105 106 # Test multiple files. 107 atf_check dd status=none if="${file}" of=bar 108 sz=$(stat -f%z bar) 109 atf_check -o inline:"0-$((hs-1)),$((hs+ps))-$((hs*3-1)) foo\n$((sz)) bar\n" \ 110 stat -h foo bar 111 112 # For a device, fail. 113 atf_check -s exit:1 -e match:"/dev/null: Illegal seek" \ 114 stat -h /dev/null 115} 116h_flag_cleanup() 117{ 118 if [ -d mnt ]; then 119 umount mnt || true 120 fi 121} 122 123atf_test_case l_flag 124l_flag_head() 125{ 126 atf_set "descr" "Verify the output format for -l" 127} 128l_flag_body() 129{ 130 atf_check touch a 131 atf_check ln a b 132 atf_check ln -s a c 133 atf_check mkdir d 134 135 paths="a b c d" 136 137 ls_out=ls.output 138 stat_out=stat.output 139 140 # NOTE: 141 # - Even though stat -l claims to be equivalent to `ls -lT`, the 142 # whitespace is a bit more liberal in the `ls -lT` output. 143 # - `ls -ldT` is used to not recursively list the contents of 144 # directories. 145 for path in $paths; do 146 atf_check -o save:$ls_out ls -ldT $path 147 cat $ls_out 148 atf_check -o save:$stat_out stat -l $path 149 cat $stat_out 150 echo "Comparing normalized whitespace" 151 atf_check sed -i '' -E -e 's/[[:space:]]+/ /g' $ls_out 152 atf_check sed -i '' -E -e 's/[[:space:]]+/ /g' $stat_out 153 atf_check cmp $ls_out $stat_out 154 done 155} 156 157atf_test_case n_flag 158n_flag_head() 159{ 160 atf_set "descr" "Verify that -n suppresses newline output for lines" 161} 162n_flag_body() 163{ 164 atf_check touch a b 165 atf_check -o inline:"$(stat a | tr -d '\n')" stat -n a 166 atf_check -o inline:"$(stat a b | tr -d '\n')" stat -n a b 167} 168 169atf_test_case q_flag 170q_flag_head() 171{ 172 atf_set "descr" "Verify that -q suppresses error messages from l?stat(2)" 173} 174q_flag_body() 175{ 176 ln -s nonexistent broken-link 177 178 atf_check -s exit:1 stat -q nonexistent 179 atf_check -s exit:1 stat -q nonexistent 180 atf_check -o not-empty stat -q broken-link 181 atf_check -o not-empty stat -qL broken-link 182} 183 184atf_test_case r_flag 185r_flag_head() 186{ 187 atf_set "descr" "Verify that -r displays output in 'raw mode'" 188} 189r_flag_body() 190{ 191 atf_check touch a 192 # TODO: add more thorough checks. 193 atf_check -o not-empty stat -r a 194} 195 196atf_test_case s_flag 197s_flag_head() 198{ 199 atf_set "descr" "Verify the output format for -s" 200} 201s_flag_body() 202{ 203 atf_check touch a 204 atf_check ln a b 205 atf_check ln -s a c 206 atf_check mkdir d 207 208 paths="a b c d" 209 210 # The order/name of each of the fields is specified by stat(1) manpage. 211 fields="st_dev st_ino st_mode st_nlink" 212 fields="$fields st_uid st_gid st_rdev st_size" 213 fields="$fields st_uid st_gid st_mode" 214 fields="$fields st_atime st_mtime st_ctime st_birthtime" 215 fields="$fields st_blksize st_blocks st_flags" 216 217 # NOTE: the following... 218 # - ... relies on set -eu to ensure that the fields are set, as 219 # documented, in stat(1). 220 # - ... uses a subshell to ensure that the eval'ed variables don't 221 # pollute the next iteration's behavior. 222 for path in $paths; do 223 ( 224 set -eu 225 eval $(stat -s $path) 226 for field in $fields; do 227 eval "$field=\$$field" 228 done 229 ) || atf_fail 'One or more fields not set by stat(1)' 230 done 231} 232 233atf_test_case t_flag 234t_flag_head() 235{ 236 atf_set "descr" "Verify the output format for -t" 237} 238t_flag_body() 239{ 240 export TZ=UTC 241 atf_check touch foo 242 atf_check touch -d 1970-01-01T00:00:42 foo 243 atf_check -o inline:'42\n' \ 244 stat -t '%s' -f '%a' foo 245 atf_check -o inline:'1970-01-01 00:00:42\n' \ 246 stat -t '%F %H:%M:%S' -f '%Sa' foo 247} 248 249x_output_date() 250{ 251 local date_format='%a %b %e %H:%M:%S %Y' 252 253 stat -t "$date_format" "$@" 254} 255 256x_output() 257{ 258 local path=$1; shift 259 260 local atime_s=$(x_output_date -f '%Sa' $path) 261 local btime_s=$(x_output_date -f '%SB' $path) 262 local ctime_s=$(x_output_date -f '%Sc' $path) 263 local devid=$(stat -f '%Hd,%Ld' $path) 264 local file_type_s=$(stat -f '%HT' $path) 265 local gid=$(stat -f '%5g' $path) 266 local groupname=$(stat -f '%8Sg' $path) 267 local inode=$(stat -f '%i' $path) 268 local mode=$(stat -f '%Mp%Lp' $path) 269 local mode_s=$(stat -f '%Sp' $path) 270 local mtime_s=$(x_output_date -f '%Sm' $path) 271 local nlink=$(stat -f '%l' $path) 272 local size_a=$(stat -f '%-11z' $path) 273 local uid=$(stat -f '%5u' $path) 274 local username=$(stat -f '%8Su' $path) 275 276 cat <<EOF 277 File: "$path" 278 Size: $size_a FileType: $file_type_s 279 Mode: ($mode/$mode_s) Uid: ($uid/$username) Gid: ($gid/$groupname) 280Device: $devid Inode: $inode Links: $nlink 281Access: $atime_s 282Modify: $mtime_s 283Change: $ctime_s 284 Birth: $btime_s 285EOF 286} 287 288atf_test_case x_flag 289x_flag_head() 290{ 291 atf_set "descr" "Verify the output format for -x" 292} 293x_flag_body() 294{ 295 atf_check touch a 296 atf_check ln a b 297 atf_check ln -s a c 298 atf_check mkdir d 299 300 paths="a b c d" 301 302 for path in $paths; do 303 atf_check -o "inline:$(x_output $path)\n" stat -x $path 304 done 305} 306 307atf_test_case devname cleanup 308devname_head() 309{ 310 atf_set "descr" "Verify that %Sd outputs a device name" 311 atf_set "require.user" "root" 312} 313devname_body() 314{ 315 local devname devpath 316 317 atf_check -o save:dev mdconfig -t malloc -s 16M 318 read devname < dev 319 devpath="/dev/$devname" 320 atf_check -o not-empty newfs "$devpath" 321 322 atf_check mkdir "$CHKPATH" 323 atf_check mount "$devpath" "$CHKPATH" 324 325 atf_check -o inline:"$devname\n" stat -f '%Sd' "$CHKPATH" 326 atf_check -o inline:"$devname\n" stat -f '%Sr' "$devpath" 327 atf_check -o inline:"$NODEV\n" stat -f '%Sr' "$CHKPATH" 328} 329devname_cleanup() 330{ 331 if [ -d "$CHKPATH" ]; then 332 umount "$CHKPATH" || true 333 fi 334 if [ -f dev ]; then 335 mdconfig -d -u $(cat dev) || true 336 fi 337} 338 339atf_init_test_cases() 340{ 341 atf_add_test_case F_flag 342 #atf_add_test_case H_flag 343 atf_add_test_case h_flag 344 #atf_add_test_case L_flag 345 #atf_add_test_case f_flag 346 atf_add_test_case l_flag 347 atf_add_test_case n_flag 348 atf_add_test_case q_flag 349 atf_add_test_case r_flag 350 atf_add_test_case s_flag 351 atf_add_test_case t_flag 352 atf_add_test_case x_flag 353 atf_add_test_case devname 354} 355