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 mkdir "${mnt}" 52} 53 54tarfs_cleanup() { 55 umount -f "${mnt}" 2>/dev/null || true 56} 57 58atf_test_case tarfs_basic cleanup 59tarfs_basic_head() { 60 atf_set "descr" "Basic function test" 61 atf_set "require.user" "root" 62 atf_set "require.kmods" "tarfs" 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.kmods" "tarfs" 91 atf_set "require.progs" "gtar" 92} 93tarfs_basic_gnu_body() { 94 TARFS_USE_GNU_TAR=true 95 tarfs_basic_body 96} 97tarfs_basic_gnu_cleanup() { 98 tarfs_basic_cleanup 99} 100 101atf_test_case tarfs_notdir_device cleanup 102tarfs_notdir_device_head() { 103 atf_set "descr" "Regression test for PR 269519 and 269561" 104 atf_set "require.user" "root" 105 atf_set "require.kmods" "tarfs" 106} 107tarfs_notdir_device_body() { 108 tarfs_setup 109 atf_check mknod d b 0xdead 0xbeef 110 tar -cf tarfs_notdir.tar d 111 rm d 112 mkdir d 113 echo "boom" >d/f 114 tar -rf tarfs_notdir.tar d/f 115 atf_check -s not-exit:0 -e match:"Invalid" \ 116 mount -rt tarfs tarfs_notdir.tar "${mnt}" 117} 118tarfs_notdir_device_cleanup() { 119 tarfs_cleanup 120} 121 122atf_test_case tarfs_notdir_device_gnu cleanup 123tarfs_notdir_device_gnu_head() { 124 atf_set "descr" "Regression test for PR 269519 and 269561 using GNU tar" 125 atf_set "require.user" "root" 126 atf_set "require.kmods" "tarfs" 127 atf_set "require.progs" "gtar" 128} 129tarfs_notdir_device_gnu_body() { 130 TARFS_USE_GNU_TAR=true 131 tarfs_notdir_device_body 132} 133tarfs_notdir_device_gnu_cleanup() { 134 tarfs_notdir_device_cleanup 135} 136 137atf_test_case tarfs_notdir_dot cleanup 138tarfs_notdir_dot_head() { 139 atf_set "descr" "Regression test for PR 269519 and 269561" 140 atf_set "require.user" "root" 141 atf_set "require.kmods" "tarfs" 142} 143tarfs_notdir_dot_body() { 144 tarfs_setup 145 echo "hello" >d 146 tar -cf tarfs_notdir.tar d 147 rm d 148 mkdir d 149 echo "world" >d/f 150 tar -rf tarfs_notdir.tar d/./f 151 atf_check -s not-exit:0 -e match:"Invalid" \ 152 mount -rt tarfs tarfs_notdir.tar "${mnt}" 153} 154tarfs_notdir_dot_cleanup() { 155 tarfs_cleanup 156} 157 158atf_test_case tarfs_notdir_dot_gnu cleanup 159tarfs_notdir_dot_gnu_head() { 160 atf_set "descr" "Regression test for PR 269519 and 269561 using GNU tar" 161 atf_set "require.user" "root" 162 atf_set "require.kmods" "tarfs" 163 atf_set "require.progs" "gtar" 164} 165tarfs_notdir_dot_gnu_body() { 166 TARFS_USE_GNU_TAR=true 167 tarfs_notdir_dot_body 168} 169tarfs_notdir_dot_gnu_cleanup() { 170 tarfs_notdir_dot_cleanup 171} 172 173atf_test_case tarfs_notdir_dotdot cleanup 174tarfs_notdir_dotdot_head() { 175 atf_set "descr" "Regression test for PR 269519 and 269561" 176 atf_set "require.user" "root" 177 atf_set "require.kmods" "tarfs" 178} 179tarfs_notdir_dotdot_body() { 180 tarfs_setup 181 echo "hello" >d 182 tar -cf tarfs_notdir.tar d 183 rm d 184 mkdir d 185 echo "world" >f 186 tar -rf tarfs_notdir.tar d/../f 187 atf_check -s not-exit:0 -e match:"Invalid" \ 188 mount -rt tarfs tarfs_notdir.tar "${mnt}" 189} 190tarfs_notdir_dotdot_cleanup() { 191 tarfs_cleanup 192} 193 194atf_test_case tarfs_notdir_dotdot_gnu cleanup 195tarfs_notdir_dotdot_gnu_head() { 196 atf_set "descr" "Regression test for PR 269519 and 269561 using GNU tar" 197 atf_set "require.user" "root" 198 atf_set "require.kmods" "tarfs" 199 atf_set "require.progs" "gtar" 200} 201tarfs_notdir_dotdot_gnu_body() { 202 TARFS_USE_GNU_TAR=true 203 tarfs_notdir_dotdot_body 204} 205tarfs_notdir_dotdot_gnu_cleanup() { 206 tarfs_notdir_dotdot_cleanup 207} 208 209atf_test_case tarfs_notdir_file cleanup 210tarfs_notdir_file_head() { 211 atf_set "descr" "Regression test for PR 269519 and 269561" 212 atf_set "require.user" "root" 213 atf_set "require.kmods" "tarfs" 214} 215tarfs_notdir_file_body() { 216 tarfs_setup 217 echo "hello" >d 218 tar -cf tarfs_notdir.tar d 219 rm d 220 mkdir d 221 echo "world" >d/f 222 tar -rf tarfs_notdir.tar d/f 223 atf_check -s not-exit:0 -e match:"Invalid" \ 224 mount -rt tarfs tarfs_notdir.tar "${mnt}" 225} 226tarfs_notdir_file_cleanup() { 227 tarfs_cleanup 228} 229 230atf_test_case tarfs_notdir_file_gnu cleanup 231tarfs_notdir_file_gnu_head() { 232 atf_set "descr" "Regression test for PR 269519 and 269561 using GNU tar" 233 atf_set "require.user" "root" 234 atf_set "require.kmods" "tarfs" 235 atf_set "require.progs" "gtar" 236} 237tarfs_notdir_file_gnu_body() { 238 TARFS_USE_GNU_TAR=true 239 tarfs_notdir_file_body 240} 241tarfs_notdir_file_gnu_cleanup() { 242 tarfs_notdir_file_cleanup 243} 244 245atf_test_case tarfs_emptylink cleanup 246tarfs_emptylink_head() { 247 atf_set "descr" "Regression test for PR 277360: empty link target" 248 atf_set "require.user" "root" 249 atf_set "require.kmods" "tarfs" 250} 251tarfs_emptylink_body() { 252 tarfs_setup 253 touch z 254 ln -f z hard 255 ln -fs z soft 256 tar -cf - z hard soft | dd bs=512 skip=1 | tr z '\0' | \ 257 tarsum >> tarfs_emptylink.tar 258 atf_check -s not-exit:0 -e match:"Invalid" \ 259 mount -rt tarfs tarfs_emptylink.tar "${mnt}" 260} 261tarfs_emptylink_cleanup() { 262 tarfs_cleanup 263} 264 265atf_test_case tarfs_linktodir cleanup 266tarfs_linktodir_head() { 267 atf_set "descr" "Regression test for PR 277360: link to directory" 268 atf_set "require.user" "root" 269 atf_set "require.kmods" "tarfs" 270} 271tarfs_linktodir_body() { 272 tarfs_setup 273 mkdir d 274 tar -cf - d | dd bs=512 count=1 > tarfs_linktodir.tar 275 rmdir d 276 touch d 277 ln -f d link 278 tar -cf - d link | dd bs=512 skip=1 >> tarfs_linktodir.tar 279 atf_check -s not-exit:0 -e match:"Invalid" \ 280 mount -rt tarfs tarfs_linktodir.tar "${mnt}" 281} 282tarfs_linktodir_cleanup() { 283 tarfs_cleanup 284} 285 286atf_test_case tarfs_linktononexistent cleanup 287tarfs_linktononexistent_head() { 288 atf_set "descr" "Regression test for PR 277360: link to nonexistent target" 289 atf_set "require.user" "root" 290 atf_set "require.kmods" "tarfs" 291} 292tarfs_linktononexistent_body() { 293 tarfs_setup 294 touch f 295 ln -f f link 296 tar -cf - f link | dd bs=512 skip=1 >> tarfs_linktononexistent.tar 297 atf_check -s not-exit:0 -e match:"Invalid" \ 298 mount -rt tarfs tarfs_linktononexistent.tar "${mnt}" 299} 300tarfs_linktononexistent_cleanup() { 301 tarfs_cleanup 302} 303 304atf_test_case tarfs_checksum cleanup 305tarfs_checksum_head() { 306 atf_set "descr" "Verify that the checksum covers header padding" 307 atf_set "require.user" "root" 308 atf_set "require.kmods" "tarfs" 309} 310tarfs_checksum_body() { 311 tarfs_setup 312 touch f 313 tar -cf tarfs_checksum.tar f 314 truncate -s 500 tarfs_checksum.tar 315 printf "\1\1\1\1\1\1\1\1\1\1\1\1" >> tarfs_checksum.tar 316 dd if=/dev/zero bs=512 count=2 >> tarfs_checksum.tar 317 hexdump -C tarfs_checksum.tar 318 atf_check -s not-exit:0 -e match:"Invalid" \ 319 mount -rt tarfs tarfs_checksum.tar "${mnt}" 320} 321tarfs_checksum_cleanup() { 322 tarfs_cleanup 323} 324 325atf_test_case tarfs_long_names cleanup 326tarfs_long_names_head() { 327 atf_set "descr" "Verify that tarfs supports long file names" 328 atf_set "require.user" "root" 329 atf_set "require.kmods" "tarfs" 330} 331tarfs_long_names_body() { 332 tarfs_setup 333 local a b c d e 334 a="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 335 b="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" 336 c="cccccccccccccccccccccccccccccccccccccccc" 337 d="dddddddddddddddddddddddddddddddddddddddd" 338 e="eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" 339 mkdir -p "${a}" 340 touch "${a}/${b}_${c}_${d}_${e}_foo" 341 ln "${a}/${b}_${c}_${d}_${e}_foo" "${a}/${b}_${c}_${d}_${e}_bar" 342 ln -s "${b}_${c}_${d}_${e}_bar" "${a}/${b}_${c}_${d}_${e}_baz" 343 tar -cf tarfs_long_names.tar "${a}" 344 atf_check mount -rt tarfs tarfs_long_names.tar "${mnt}" 345} 346tarfs_long_names_cleanup() { 347 tarfs_cleanup 348} 349 350atf_test_case tarfs_long_paths cleanup 351tarfs_long_paths_head() { 352 atf_set "descr" "Verify that tarfs supports long paths" 353 atf_set "require.user" "root" 354 atf_set "require.kmods" "tarfs" 355} 356tarfs_long_paths_body() { 357 tarfs_setup 358 local a b c d e 359 a="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 360 b="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" 361 c="cccccccccccccccccccccccccccccccccccccccc" 362 d="dddddddddddddddddddddddddddddddddddddddd" 363 e="eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" 364 mkdir -p "${a}/${b}/${c}/${d}/${e}" 365 touch "${a}/${b}/${c}/${d}/${e}/foo" 366 ln "${a}/${b}/${c}/${d}/${e}/foo" "${a}/${b}/${c}/${d}/${e}/bar" 367 ln -s "${b}/${c}/${d}/${e}/bar" "${a}/baz" 368 tar -cf tarfs_long_paths.tar "${a}" 369 atf_check mount -rt tarfs tarfs_long_paths.tar "${mnt}" 370} 371tarfs_long_paths_cleanup() { 372 tarfs_cleanup 373} 374 375atf_test_case tarfs_git_archive cleanup 376tarfs_git_archive_head() { 377 atf_set "descr" "Verify that tarfs supports archives created by git" 378 atf_set "require.user" "root" 379 atf_set "require.kmods" "tarfs" 380 atf_set "require.progs" "git" 381} 382tarfs_git_archive_body() { 383 tarfs_setup 384 mkdir foo 385 echo "Hello, world!" >foo/bar 386 git -C foo init --initial-branch=tarfs 387 git -C foo config user.name "File System" 388 git -C foo config user.email fs@freebsd.org 389 git -C foo add bar 390 git -C foo commit -m bar 391 git -C foo archive --output=../tarfs_git_archive.tar HEAD 392 atf_check mount -rt tarfs tarfs_git_archive.tar "${mnt}" 393 atf_check -o file:foo/bar cat "${mnt}"/bar 394} 395tarfs_git_archive_cleanup() { 396 tarfs_cleanup 397} 398 399atf_init_test_cases() { 400 atf_add_test_case tarfs_basic 401 atf_add_test_case tarfs_basic_gnu 402 atf_add_test_case tarfs_notdir_device 403 atf_add_test_case tarfs_notdir_device_gnu 404 atf_add_test_case tarfs_notdir_dot 405 atf_add_test_case tarfs_notdir_dot_gnu 406 atf_add_test_case tarfs_notdir_dotdot 407 atf_add_test_case tarfs_notdir_dotdot_gnu 408 atf_add_test_case tarfs_notdir_file 409 atf_add_test_case tarfs_notdir_file_gnu 410 atf_add_test_case tarfs_emptylink 411 atf_add_test_case tarfs_linktodir 412 atf_add_test_case tarfs_linktononexistent 413 atf_add_test_case tarfs_checksum 414 atf_add_test_case tarfs_long_names 415 atf_add_test_case tarfs_long_paths 416 atf_add_test_case tarfs_git_archive 417} 418