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