1#!/bin/sh 2#- 3# SPDX-License-Identifier: BSD-2-Clause 4# 5# Copyright (c) 2023-2024 Klara, Inc. 6# 7# Redistribution and use in source and binary forms, with or without 8# modification, are permitted provided that the following conditions 9# are met: 10# 1. Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# 2. Redistributions in binary form must reproduce the above copyright 13# notice, this list of conditions and the following disclaimer in the 14# documentation and/or other materials provided with the distribution. 15# 16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26# SUCH DAMAGE. 27# 28 29mnt="$(realpath ${TMPDIR:-/tmp})/mnt" 30 31# expected SHA256 checksum of file contained in test tarball 32sum=4da2143234486307bb44eaa610375301781a577d1172f362b88bb4b1643dee62 33 34tar() { 35 if [ -n "${TARFS_USE_GNU_TAR}" ] ; then 36 gtar --posix --absolute-names "$@" 37 else 38 bsdtar "$@" 39 fi 40} 41 42mktar() { 43 "$(atf_get_srcdir)"/mktar ${TARFS_USE_GNU_TAR+-g} "$@" 44} 45 46tarsum() { 47 "$(atf_get_srcdir)"/tarsum 48} 49 50tarfs_setup() { 51 kldload -n tarfs || atf_skip "This test requires tarfs and could not load it" 52 mkdir "${mnt}" 53} 54 55tarfs_cleanup() { 56 umount -f "${mnt}" 2>/dev/null || true 57} 58 59atf_test_case tarfs_basic cleanup 60tarfs_basic_head() { 61 atf_set "descr" "Basic function test" 62 atf_set "require.user" "root" 63} 64tarfs_basic_body() { 65 tarfs_setup 66 local tarball="${PWD}/tarfs_test.tar.zst" 67 mktar "${tarball}" 68 atf_check mount -rt tarfs "${tarball}" "${mnt}" 69 atf_check -o match:"^${tarball} on ${mnt} \(tarfs," mount 70 atf_check_equal "$(stat -f%d,%i "${mnt}"/sparse_file)" "$(stat -f%d,%i "${mnt}"/hard_link)" 71 atf_check_equal "$(stat -f%d,%i "${mnt}"/sparse_file)" "$(stat -L -f%d,%i "${mnt}"/short_link)" 72 atf_check_equal "$(stat -f%d,%i "${mnt}"/sparse_file)" "$(stat -L -f%d,%i "${mnt}"/long_link)" 73 atf_check -o inline:"${sum}\n" sha256 -q "${mnt}"/sparse_file 74 atf_check -o inline:"2,40755\n" stat -f%l,%p "${mnt}"/directory 75 atf_check -o inline:"1,100644\n" stat -f%l,%p "${mnt}"/file 76 atf_check -o inline:"2,100644\n" stat -f%l,%p "${mnt}"/hard_link 77 atf_check -o inline:"1,120755\n" stat -f%l,%p "${mnt}"/long_link 78 atf_check -o inline:"1,120755\n" stat -f%l,%p "${mnt}"/short_link 79 atf_check -o inline:"2,100644\n" stat -f%l,%p "${mnt}"/sparse_file 80 atf_check -o inline:"3,40755\n" stat -f%l,%p "${mnt}" 81} 82tarfs_basic_cleanup() { 83 tarfs_cleanup 84} 85 86atf_test_case tarfs_basic_gnu cleanup 87tarfs_basic_gnu_head() { 88 atf_set "descr" "Basic function test using GNU tar" 89 atf_set "require.user" "root" 90 atf_set "require.progs" "gtar" 91} 92tarfs_basic_gnu_body() { 93 TARFS_USE_GNU_TAR=true 94 tarfs_basic_body 95} 96tarfs_basic_gnu_cleanup() { 97 tarfs_basic_cleanup 98} 99 100atf_test_case tarfs_notdir_device cleanup 101tarfs_notdir_device_head() { 102 atf_set "descr" "Regression test for PR 269519 and 269561" 103 atf_set "require.user" "root" 104} 105tarfs_notdir_device_body() { 106 tarfs_setup 107 atf_check mknod d b 0xdead 0xbeef 108 tar -cf tarfs_notdir.tar d 109 rm d 110 mkdir d 111 echo "boom" >d/f 112 tar -rf tarfs_notdir.tar d/f 113 atf_check -s not-exit:0 -e match:"Invalid" \ 114 mount -rt tarfs tarfs_notdir.tar "${mnt}" 115} 116tarfs_notdir_device_cleanup() { 117 tarfs_cleanup 118} 119 120atf_test_case tarfs_notdir_device_gnu cleanup 121tarfs_notdir_device_gnu_head() { 122 atf_set "descr" "Regression test for PR 269519 and 269561 using GNU tar" 123 atf_set "require.user" "root" 124 atf_set "require.progs" "gtar" 125} 126tarfs_notdir_device_gnu_body() { 127 TARFS_USE_GNU_TAR=true 128 tarfs_notdir_device_body 129} 130tarfs_notdir_device_gnu_cleanup() { 131 tarfs_notdir_device_cleanup 132} 133 134atf_test_case tarfs_notdir_dot cleanup 135tarfs_notdir_dot_head() { 136 atf_set "descr" "Regression test for PR 269519 and 269561" 137 atf_set "require.user" "root" 138} 139tarfs_notdir_dot_body() { 140 tarfs_setup 141 echo "hello" >d 142 tar -cf tarfs_notdir.tar d 143 rm d 144 mkdir d 145 echo "world" >d/f 146 tar -rf tarfs_notdir.tar d/./f 147 atf_check -s not-exit:0 -e match:"Invalid" \ 148 mount -rt tarfs tarfs_notdir.tar "${mnt}" 149} 150tarfs_notdir_dot_cleanup() { 151 tarfs_cleanup 152} 153 154atf_test_case tarfs_notdir_dot_gnu cleanup 155tarfs_notdir_dot_gnu_head() { 156 atf_set "descr" "Regression test for PR 269519 and 269561 using GNU tar" 157 atf_set "require.user" "root" 158 atf_set "require.progs" "gtar" 159} 160tarfs_notdir_dot_gnu_body() { 161 TARFS_USE_GNU_TAR=true 162 tarfs_notdir_dot_body 163} 164tarfs_notdir_dot_gnu_cleanup() { 165 tarfs_notdir_dot_cleanup 166} 167 168atf_test_case tarfs_notdir_dotdot cleanup 169tarfs_notdir_dotdot_head() { 170 atf_set "descr" "Regression test for PR 269519 and 269561" 171 atf_set "require.user" "root" 172} 173tarfs_notdir_dotdot_body() { 174 tarfs_setup 175 echo "hello" >d 176 tar -cf tarfs_notdir.tar d 177 rm d 178 mkdir d 179 echo "world" >f 180 tar -rf tarfs_notdir.tar d/../f 181 atf_check -s not-exit:0 -e match:"Invalid" \ 182 mount -rt tarfs tarfs_notdir.tar "${mnt}" 183} 184tarfs_notdir_dotdot_cleanup() { 185 tarfs_cleanup 186} 187 188atf_test_case tarfs_notdir_dotdot_gnu cleanup 189tarfs_notdir_dotdot_gnu_head() { 190 atf_set "descr" "Regression test for PR 269519 and 269561 using GNU tar" 191 atf_set "require.user" "root" 192 atf_set "require.progs" "gtar" 193} 194tarfs_notdir_dotdot_gnu_body() { 195 TARFS_USE_GNU_TAR=true 196 tarfs_notdir_dotdot_body 197} 198tarfs_notdir_dotdot_gnu_cleanup() { 199 tarfs_notdir_dotdot_cleanup 200} 201 202atf_test_case tarfs_notdir_file cleanup 203tarfs_notdir_file_head() { 204 atf_set "descr" "Regression test for PR 269519 and 269561" 205 atf_set "require.user" "root" 206} 207tarfs_notdir_file_body() { 208 tarfs_setup 209 echo "hello" >d 210 tar -cf tarfs_notdir.tar d 211 rm d 212 mkdir d 213 echo "world" >d/f 214 tar -rf tarfs_notdir.tar d/f 215 atf_check -s not-exit:0 -e match:"Invalid" \ 216 mount -rt tarfs tarfs_notdir.tar "${mnt}" 217} 218tarfs_notdir_file_cleanup() { 219 tarfs_cleanup 220} 221 222atf_test_case tarfs_notdir_file_gnu cleanup 223tarfs_notdir_file_gnu_head() { 224 atf_set "descr" "Regression test for PR 269519 and 269561 using GNU tar" 225 atf_set "require.user" "root" 226 atf_set "require.progs" "gtar" 227} 228tarfs_notdir_file_gnu_body() { 229 TARFS_USE_GNU_TAR=true 230 tarfs_notdir_file_body 231} 232tarfs_notdir_file_gnu_cleanup() { 233 tarfs_notdir_file_cleanup 234} 235 236atf_test_case tarfs_emptylink cleanup 237tarfs_emptylink_head() { 238 atf_set "descr" "Regression test for PR 277360: empty link target" 239 atf_set "require.user" "root" 240} 241tarfs_emptylink_body() { 242 tarfs_setup 243 touch z 244 ln -f z hard 245 ln -fs z soft 246 tar -cf - z hard soft | dd bs=512 skip=1 | tr z '\0' | \ 247 tarsum >> tarfs_emptylink.tar 248 atf_check -s not-exit:0 -e match:"Invalid" \ 249 mount -rt tarfs tarfs_emptylink.tar "${mnt}" 250} 251tarfs_emptylink_cleanup() { 252 tarfs_cleanup 253} 254 255atf_test_case tarfs_linktodir cleanup 256tarfs_linktodir_head() { 257 atf_set "descr" "Regression test for PR 277360: link to directory" 258 atf_set "require.user" "root" 259} 260tarfs_linktodir_body() { 261 tarfs_setup 262 mkdir d 263 tar -cf - d | dd bs=512 count=1 > tarfs_linktodir.tar 264 rmdir d 265 touch d 266 ln -f d link 267 tar -cf - d link | dd bs=512 skip=1 >> tarfs_linktodir.tar 268 atf_check -s not-exit:0 -e match:"Invalid" \ 269 mount -rt tarfs tarfs_linktodir.tar "${mnt}" 270} 271tarfs_linktodir_cleanup() { 272 tarfs_cleanup 273} 274 275atf_test_case tarfs_linktononexistent cleanup 276tarfs_linktononexistent_head() { 277 atf_set "descr" "Regression test for PR 277360: link to nonexistent target" 278 atf_set "require.user" "root" 279} 280tarfs_linktononexistent_body() { 281 tarfs_setup 282 touch f 283 ln -f f link 284 tar -cf - f link | dd bs=512 skip=1 >> tarfs_linktononexistent.tar 285 atf_check -s not-exit:0 -e match:"Invalid" \ 286 mount -rt tarfs tarfs_linktononexistent.tar "${mnt}" 287} 288tarfs_linktononexistent_cleanup() { 289 tarfs_cleanup 290} 291 292atf_test_case tarfs_checksum cleanup 293tarfs_checksum_head() { 294 atf_set "descr" "Verify that the checksum covers header padding" 295 atf_set "require.user" "root" 296} 297tarfs_checksum_body() { 298 tarfs_setup 299 touch f 300 tar -cf tarfs_checksum.tar f 301 truncate -s 500 tarfs_checksum.tar 302 printf "\1\1\1\1\1\1\1\1\1\1\1\1" >> tarfs_checksum.tar 303 dd if=/dev/zero bs=512 count=2 >> tarfs_checksum.tar 304 hexdump -C tarfs_checksum.tar 305 atf_check -s not-exit:0 -e match:"Invalid" \ 306 mount -rt tarfs tarfs_checksum.tar "${mnt}" 307} 308tarfs_checksum_cleanup() { 309 tarfs_cleanup 310} 311 312atf_test_case tarfs_long_names cleanup 313tarfs_long_names_head() { 314 atf_set "descr" "Verify that tarfs supports long file names" 315 atf_set "require.user" "root" 316} 317tarfs_long_names_body() { 318 tarfs_setup 319 local a b c d e 320 a="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 321 b="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" 322 c="cccccccccccccccccccccccccccccccccccccccc" 323 d="dddddddddddddddddddddddddddddddddddddddd" 324 e="eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" 325 mkdir -p "${a}" 326 touch "${a}/${b}_${c}_${d}_${e}_foo" 327 ln "${a}/${b}_${c}_${d}_${e}_foo" "${a}/${b}_${c}_${d}_${e}_bar" 328 ln -s "${b}_${c}_${d}_${e}_bar" "${a}/${b}_${c}_${d}_${e}_baz" 329 tar -cf tarfs_long_names.tar "${a}" 330 atf_check mount -rt tarfs tarfs_long_names.tar "${mnt}" 331} 332tarfs_long_names_cleanup() { 333 tarfs_cleanup 334} 335 336atf_test_case tarfs_long_paths cleanup 337tarfs_long_paths_head() { 338 atf_set "descr" "Verify that tarfs supports long paths" 339 atf_set "require.user" "root" 340} 341tarfs_long_paths_body() { 342 tarfs_setup 343 local a b c d e 344 a="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 345 b="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" 346 c="cccccccccccccccccccccccccccccccccccccccc" 347 d="dddddddddddddddddddddddddddddddddddddddd" 348 e="eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" 349 mkdir -p "${a}/${b}/${c}/${d}/${e}" 350 touch "${a}/${b}/${c}/${d}/${e}/foo" 351 ln "${a}/${b}/${c}/${d}/${e}/foo" "${a}/${b}/${c}/${d}/${e}/bar" 352 ln -s "${b}/${c}/${d}/${e}/bar" "${a}/baz" 353 tar -cf tarfs_long_paths.tar "${a}" 354 atf_check mount -rt tarfs tarfs_long_paths.tar "${mnt}" 355} 356tarfs_long_paths_cleanup() { 357 tarfs_cleanup 358} 359 360atf_test_case tarfs_git_archive cleanup 361tarfs_git_archive_head() { 362 atf_set "descr" "Verify that tarfs supports archives created by git" 363 atf_set "require.user" "root" 364 atf_set "require.progs" "git" 365} 366tarfs_git_archive_body() { 367 tarfs_setup 368 mkdir foo 369 echo "Hello, world!" >foo/bar 370 git -C foo init --initial-branch=tarfs 371 git -C foo config user.name "File System" 372 git -C foo config user.email fs@freebsd.org 373 git -C foo add bar 374 git -C foo commit -m bar 375 git -C foo archive --output=../tarfs_git_archive.tar HEAD 376 atf_check mount -rt tarfs tarfs_git_archive.tar "${mnt}" 377 atf_check -o file:foo/bar cat "${mnt}"/bar 378} 379tarfs_git_archive_cleanup() { 380 tarfs_cleanup 381} 382 383atf_init_test_cases() { 384 atf_add_test_case tarfs_basic 385 atf_add_test_case tarfs_basic_gnu 386 atf_add_test_case tarfs_notdir_device 387 atf_add_test_case tarfs_notdir_device_gnu 388 atf_add_test_case tarfs_notdir_dot 389 atf_add_test_case tarfs_notdir_dot_gnu 390 atf_add_test_case tarfs_notdir_dotdot 391 atf_add_test_case tarfs_notdir_dotdot_gnu 392 atf_add_test_case tarfs_notdir_file 393 atf_add_test_case tarfs_notdir_file_gnu 394 atf_add_test_case tarfs_emptylink 395 atf_add_test_case tarfs_linktodir 396 atf_add_test_case tarfs_linktononexistent 397 atf_add_test_case tarfs_checksum 398 atf_add_test_case tarfs_long_names 399 atf_add_test_case tarfs_long_paths 400 atf_add_test_case tarfs_git_archive 401} 402