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