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