1#!/bin/sh 2# SPDX-License-Identifier: GPL-2.0-or-later 3# Copyright (c) International Business Machines Corp., 2005 4# Copyright (c) 2021 Joerg Vehlow <joerg.vehlow@aox-tech.de> 5# Based on work by: Avantika Mathur (mathurav@us.ibm.com) 6 7TST_NEEDS_TMPDIR=1 8TST_NEEDS_ROOT=1 9TST_MIN_KVER=2.6.15 10TST_SETUP=fs_bind_setup 11TST_CLEANUP=fs_bind_cleanup 12TST_TESTFUNC=fs_bind_test 13TST_NEEDS_CMDS="mount umount awk sed" 14 15. tst_test.sh 16 17[ -z "$FS_BIND_TESTFUNC" ] && tst_brk TBROK "Please set FS_BIND_TESTFUNC before sourcing fs_bind_lib.sh" 18 19# Test interface: 20# 21# FS_BIND_TESTFUNC is the real testfunction. Please do not use 22# TST_TESTFUNC in the test. FS_BIND_TESTFUNC is used to wrap some additional logic. 23# TST_CNT can be used as usual 24 25FS_BIND_SANDBOX="sandbox" 26FS_BIND_DISK1="disk1" 27FS_BIND_DISK2="disk2" 28FS_BIND_DISK3="disk3" 29FS_BIND_DISK4="disk4" 30 31FS_BIND_MNTNS_PID= 32 33# Creates a directory and bind-mounts it to itself. 34# usage: fs_bind_makedir share_mode directory 35# where 36# share_mode is one of the --make-{shared,priv,...} 37# supported by mount 38# directory is directory where to be created/configured. 39# If it does not exist, it will be created 40fs_bind_makedir() 41{ 42 local bind_type dir 43 44 bind_type="$1" 45 dir="$2" 46 47 case "$bind_type" in 48 shared|private|rshared|slave|rslave|rprivate|runbindable) ;; 49 *) tst_brk TBROK "Unknown share type \"$bind_type\"" 50 esac 51 52 if [ -e "$dir" ]; then 53 tst_brk TBROK "An entry by the name \"$dir\" exists already" 54 fi 55 56 ROD mkdir -p "$dir" 57 58 # Most mount implementations can use --make-* in the same command as bind, 59 # but busybox mount, fails at least to set --make-private 60 EXPECT_PASS mount --bind "$dir" "$dir" 61 EXPECT_PASS mount --make-$bind_type "$dir" 62} 63 64# Verifies that subtrees contain the same content 65# usage: fs_bind_check [-n] dir1 dirn... 66# where 67# -n If set, expectes the subtrees to not be equal 68# -s Run check in mount namespace 69# dir1 dirn... An arbitraty number of directories, that are compared. 70# If -n is given, only two directories are allowed. 71fs_bind_check() 72{ 73 local OPTIND expect_diff use_ns args msg dir1 dir2 fail output 74 expect_diff=0 75 use_ns=0 76 while getopts "ns" args; do 77 case "$args" in 78 n) expect_diff=1; ;; 79 s) use_ns=1; ;; 80 esac 81 done 82 shift $((OPTIND-1)) 83 msg="Check" 84 [ $expect_diff -eq 1 ] && msg="$msg no" 85 msg="$msg propagation" 86 if [ $use_ns -eq 1 ]; then 87 [ -z "$FS_BIND_MNTNS_PID" ] && tst_brk TBROK "Namespace does not exist" 88 msg="$msg in mnt namespace" 89 fi 90 msg="$msg $*" 91 92 if [ $# -lt 2 ] || ( [ $expect_diff -eq 1 ] && [ $# -ne 2 ] ); then 93 tst_brk TBROK "Insufficient arguments" 94 fi 95 96 dir1=$1 97 shift 98 99 for dir2 in "$@"; do 100 # compare adjacent pairs of directory trees 101 102 if [ ! -d "$dir1" ]; then 103 tst_res TFAIL "$msg: \"$dir1\" does not exist" 104 return 1 105 elif [ ! -d "$dir2" ]; then 106 if [ $expect_diff -eq 1 ]; then 107 tst_res TPASS "$msg" 108 return 0 109 else 110 tst_res TFAIL "$msg: \"$dir2\" does not exist" 111 return 1 112 fi 113 fi 114 115 if [ $use_ns -eq 1 ]; then 116 output="$(ns_exec ${FS_BIND_MNTNS_PID} mnt diff -r "$PWD/$dir1" "$PWD/$dir2" 2> /dev/null)" 117 else 118 output="$(diff -r "$dir1" "$dir2" 2> /dev/null)" 119 fi 120 121 if [ $? -ne 0 ]; then 122 if [ $expect_diff -eq 1 ]; then 123 # Since expect_diff=1 allows only two directories 124 # to be compared, we are done after finding the first diff 125 tst_res TPASS "$msg" 126 return 0 127 else 128 tst_res TFAIL "$msg:" 129 tst_res TFAIL "\"$dir1\" \"$dir2\" differ:\n$output" 130 return 1 131 fi 132 fi 133 dir1="$dir2" 134 done 135 136 if [ $expect_diff -eq 1 ]; then 137 tst_res TFAIL "$msg" 138 return 1 139 else 140 tst_res TPASS "$msg" 141 return 0 142 fi 143} 144 145fs_bind_setup() 146{ 147 fs_bind_makedir private "$FS_BIND_SANDBOX" 148 149 cd $FS_BIND_SANDBOX 150 mkdir -p "$FS_BIND_DISK1" "$FS_BIND_DISK2" "$FS_BIND_DISK3" "$FS_BIND_DISK4" 151 mkdir "$FS_BIND_DISK1/a" "$FS_BIND_DISK1/b" "$FS_BIND_DISK1/c" 152 mkdir "$FS_BIND_DISK2/d" "$FS_BIND_DISK2/e" "$FS_BIND_DISK2/f" 153 mkdir "$FS_BIND_DISK3/g" "$FS_BIND_DISK3/h" "$FS_BIND_DISK3/i" 154 mkdir "$FS_BIND_DISK4/j" "$FS_BIND_DISK4/k" "$FS_BIND_DISK4/l" 155} 156 157_fs_bind_unmount_all() 158{ 159 local mounts 160 161 cd "$TST_TMPDIR" 162 163 # Cleanup leftover mounts from the test 164 # sed '1!G;h;$!d' is used to reverse /proc/mounts. 165 # unmounting in reverse order requires significantly less iterations 166 # There is a slight chance, this loop does not terminate, if a mount 167 # cannot be unmounted for some reason. In that case the timeout 168 # will kill the test, but we cannot restore the system anyway 169 while true; do 170 mounts=$( sed '1!G;h;$!d' /proc/mounts \ 171 | awk -vtmp="$TST_TMPDIR/$FS_BIND_SANDBOX/" \ 172 'index($2, tmp) {print $2}' ) 173 [ -z "$mounts" ] && break 174 echo $mounts | xargs umount 2>/dev/null 175 done 176} 177 178fs_bind_cleanup() 179{ 180 _fs_bind_unmount_all 181 umount "$FS_BIND_SANDBOX" 182} 183 184_fs_bind_setup_test() 185{ 186 local e 187 188 cd "$TST_TMPDIR/$FS_BIND_SANDBOX" || tst_brk "Unable to cd into sandbox" 189 190 for e in ls *; do 191 if [ "$e" = "$FS_BIND_DISK1" ] \ 192 || [ "$e" = "$FS_BIND_DISK2" ] \ 193 || [ "$e" = "$FS_BIND_DISK3" ] \ 194 || [ "$e" = "$FS_BIND_DISK4" ]; then 195 continue 196 fi 197 rm -rf "$e" 198 done 199} 200 201fs_bind_create_ns() 202{ 203 [ -n "$FS_BIND_MNTNS_PID" ] && tst_brk TBROK "Namespace exist already" 204 FS_BIND_MNTNS_PID=$(ns_create mnt) 205} 206 207fs_bind_exec_ns() 208{ 209 [ -z "$FS_BIND_MNTNS_PID" ] && tst_brk TBROK "Namespace does not exist" 210 EXPECT_PASS ns_exec $FS_BIND_MNTNS_PID mnt "$@" 211} 212 213fs_bind_destroy_ns() 214{ 215 [ -n "$FS_BIND_MNTNS_PID" ] && kill $FS_BIND_MNTNS_PID 2>/dev/null 216 FS_BIND_MNTNS_PID= 217} 218 219_fs_bind_cleanup_test() 220{ 221 local mounts 222 223 fs_bind_destroy_ns 224 225 mounts=$( awk -v tmp="$TST_TMPDIR/$FS_BIND_SANDBOX/" ' 226 index($2, tmp) { 227 print substr($2, length(tmp) + 1) 228 } 229 ' /proc/mounts ) 230 if [ -n "$mounts" ]; then 231 tst_res TFAIL "There are still mounts in the sandbox:\n$mounts" 232 fi 233 _fs_bind_unmount_all 234} 235 236fs_bind_test() 237{ 238 _fs_bind_setup_test 239 240 if type ${FS_BIND_TESTFUNC}1 > /dev/null 2>&1; then 241 "$FS_BIND_TESTFUNC$_tst_i" $1 242 else 243 "$FS_BIND_TESTFUNC" $1 244 fi 245 246 _fs_bind_cleanup_test 247} 248