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