1#!/bin/bash
2
3function print_usage()
4{
5    if [ -n "$2" ]; then
6        echo "Error: $2"
7        echo
8    fi
9    echo "Usage: $1 <shared dir>"
10    echo '(The shared directory is the "share" directory in the scratch' \
11         'directory)'
12}
13
14shared_dir=$1
15if [ -z "$shared_dir" ]; then
16    print_usage "$0" 'Shared dir not given' >&2
17    exit 1
18fi
19
20cd "$shared_dir"
21
22# FIXME: This should not be necessary, but it is.  In order for all
23# submounts to be proper mount points, we need to visit them.
24# (Before we visit them, they will not be auto-mounted, and so just
25# appear as normal directories, with the catch that their st_ino will
26# be the st_ino of the filesystem they host, while the st_dev will
27# still be the st_dev of the parent.)
28# `find` does not work, because it will refuse to touch the mount
29# points as long as they are not mounted; their st_dev being shared
30# with the parent and st_ino just being the root node's inode ID
31# will practically ensure that this node exists elsewhere on the
32# filesystem, and `find` is required to recognize loops and not to
33# follow them.
34# Thus, we have to manually visit all nodes first.
35
36mnt_i=0
37
38function recursively_visit()
39{
40    pushd "$1" >/dev/null
41    for entry in *; do
42        if [[ "$entry" == mnt* ]]; then
43            mnt_i=$((mnt_i + 1))
44            printf "Triggering auto-mount $mnt_i...\r"
45        fi
46
47        if [ -d "$entry" ]; then
48            recursively_visit "$entry"
49        fi
50    done
51    popd >/dev/null
52}
53
54recursively_visit .
55echo
56
57
58if [ -n "$(find -name not-mounted)" ]; then
59    echo "Error: not-mounted files visible on mount points:" >&2
60    find -name not-mounted >&2
61    exit 1
62fi
63
64if [ ! -f some-file -o "$(cat some-file)" != 'root' ]; then
65    echo "Error: Bad file in the share root" >&2
66    exit 1
67fi
68
69shopt -s nullglob
70
71function check_submounts()
72{
73    local base_path=$1
74
75    for mp in mnt*; do
76        printf "Checking submount %i...\r" "$((${#devs[@]} + 1))"
77
78        mp_i=$(echo "$mp" | sed -e 's/mnt//')
79        dev=$(stat -c '%D' "$mp")
80
81        if [ -n "${devs[mp_i]}" ]; then
82            echo "Error: $mp encountered twice" >&2
83            exit 1
84        fi
85        devs[mp_i]=$dev
86
87        pushd "$mp" >/dev/null
88        path="$base_path$mp"
89        while true; do
90            expected_content="$(printf '%s\n%s\n' "$mp_i" "$path")"
91            if [ ! -f some-file ]; then
92                echo "Error: $PWD/some-file does not exist" >&2
93                exit 1
94            fi
95
96            if [ "$(cat some-file)" != "$expected_content" ]; then
97                echo "Error: Bad content in $PWD/some-file:" >&2
98                echo '--- found ---'
99                cat some-file
100                echo '--- expected ---'
101                echo "$expected_content"
102                exit 1
103            fi
104            if [ "$(stat -c '%D' some-file)" != "$dev" ]; then
105                echo "Error: $PWD/some-file has the wrong device ID" >&2
106                exit 1
107            fi
108
109            if [ -d sub ]; then
110                if [ "$(stat -c '%D' sub)" != "$dev" ]; then
111                    echo "Error: $PWD/some-file has the wrong device ID" >&2
112                    exit 1
113                fi
114                cd sub
115                path="$path/sub"
116            else
117                if [ -n "$(echo mnt*)" ]; then
118                    check_submounts "$path/"
119                fi
120                break
121            fi
122        done
123        popd >/dev/null
124    done
125}
126
127root_dev=$(stat -c '%D' some-file)
128devs=()
129check_submounts ''
130echo
131
132reused_devs=$(echo "$root_dev ${devs[@]}" | tr ' ' '\n' | sort | uniq -d)
133if [ -n "$reused_devs" ]; then
134    echo "Error: Reused device IDs: $reused_devs" >&2
135    exit 1
136fi
137
138echo "Test passed for ${#devs[@]} submounts."
139