#!/usr/bin/env bats

load helpers

function setup() {
	OVERFLOW_UID="$(cat /proc/sys/kernel/overflowuid)"
	OVERFLOW_GID="$(cat /proc/sys/kernel/overflowgid)"
	requires root
	requires_kernel 5.12

	setup_debian
	requires_idmap_fs .

	# Prepare source folders for mounts.
	mkdir -p source-{1,2,multi{1,2,3}}/
	touch source-{1,2,multi{1,2,3}}/foo.txt
	touch source-multi{1,2,3}/{bar,baz}.txt

	# Change the owners for everything other than source-1.
	chown 1:1 source-2/foo.txt

	# A source with multiple users owning files.
	chown 100:211 source-multi1/foo.txt
	chown 101:222 source-multi1/bar.txt
	chown 102:233 source-multi1/baz.txt

	# Same gids as multi1, different uids.
	chown 200:211 source-multi2/foo.txt
	chown 201:222 source-multi2/bar.txt
	chown 202:233 source-multi2/baz.txt

	# Even more users -- 1000 uids, 500 gids.
	chown 5000528:6000491 source-multi3/foo.txt
	chown 5000133:6000337 source-multi3/bar.txt
	chown 5000999:6000444 source-multi3/baz.txt

	# Add a symlink-containing source.
	ln -s source-multi1 source-multi1-symlink

	# Add some top-level files in the mount tree.
	mkdir -p mnt-subtree/multi{1,2}
	touch mnt-subtree/{foo,bar,baz}.txt
	chown 100:211 mnt-subtree/foo.txt
	chown 200:222 mnt-subtree/bar.txt
	chown 300:233 mnt-subtree/baz.txt

	mounts_file="$PWD/.all-mounts"
	echo -n >"$mounts_file"
}

function teardown() {
	if [ -v mounts_file ]; then
		xargs -n 1 -a "$mounts_file" -- umount -l
		rm -f "$mounts_file"
	fi
	teardown_bundle
}

function setup_host_bind_mount() {
	src="$1"
	dst="$2"

	mount --bind "$src" "$dst"
	echo "$dst" >>"$mounts_file"
}

function setup_idmap_userns() {
	update_config '.linux.namespaces += [{"type": "user"}]
		| .linux.uidMappings += [{"containerID": 0, "hostID": 100000, "size": 65536}]
		| .linux.gidMappings += [{"containerID": 0, "hostID": 100000, "size": 65536}]'
	remap_rootfs
}

function setup_bind_mount() {
	mountname="${1:-1}"
	update_config '.mounts += [
			{
				"source": "source-'"$mountname"'/",
				"destination": "/tmp/bind-mount-'"$mountname"'",
				"options": ["bind"]
			}
		]'
}

function setup_idmap_single_mount() {
	uidmap="$1" # ctr:host:size
	gidmap="$2" # ctr:host:size
	mountname="$3"
	destname="${4:-$mountname}"

	read -r uid_containerID uid_hostID uid_size <<<"$(tr : ' ' <<<"$uidmap")"
	read -r gid_containerID gid_hostID gid_size <<<"$(tr : ' ' <<<"$gidmap")"

	update_config '.mounts += [
			{
				"source": "source-'"$mountname"'/",
				"destination": "/tmp/mount-'"$destname"'",
				"options": ["bind"],
				"uidMappings": [{"containerID": '"$uid_containerID"', "hostID": '"$uid_hostID"', "size": '"$uid_size"'}],
				"gidMappings": [{"containerID": '"$gid_containerID"', "hostID": '"$gid_hostID"', "size": '"$gid_size"'}]
			}
		]'
}

function setup_idmap_basic_mount() {
	mountname="${1:-1}"
	destname="${2:-}"
	setup_idmap_single_mount 0:100000:65536 0:100000:65536 "$mountname" "$destname"
}

@test "Check mount source fds are cleaned up with idmapped mounts [userns]" {
	setup_idmap_userns
	for i in {1..10}; do
		setup_idmap_basic_mount 1 "1$i"
		setup_idmap_basic_mount 2 "2$i"
	done

	update_config '.process.args = ["sh", "-c", "stat -c =%u=%g= /tmp/mount-11/foo.txt"]'
	update_config '.process.rlimits = [{
		"type": "RLIMIT_NOFILE",
		"soft": 20,
		"hard": 20
	}]'

	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=0=0="* ]]
}

@test "simple idmap mount [userns]" {
	setup_idmap_userns
	setup_idmap_basic_mount

	update_config '.process.args = ["sh", "-c", "stat -c =%u=%g= /tmp/mount-1/foo.txt"]'

	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=0=0="* ]]
}

@test "simple idmap mount [no userns]" {
	setup_idmap_basic_mount

	update_config '.process.args = ["sh", "-c", "stat -c =%u=%g= /tmp/mount-1/foo.txt"]'

	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=100000=100000="* ]]
}

@test "write to an idmap mount [userns]" {
	setup_idmap_userns
	setup_idmap_basic_mount

	update_config '.process.args = ["sh", "-c", "touch /tmp/mount-1/bar && stat -c =%u=%g= /tmp/mount-1/bar"]'

	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=0=0="* ]]
}

@test "write to an idmap mount [no userns]" {
	setup_idmap_basic_mount

	update_config '.process.args = ["sh", "-c", "touch /tmp/mount-1/bar && stat -c =%u=%g= /tmp/mount-1/bar"]'

	runc run test_debian
	# The write must fail because the user is unmapped.
	[ "$status" -ne 0 ]
	[[ "$output" == *"Value too large for defined data type"* ]] # ERANGE
}

@test "idmap mount with propagation flag [userns]" {
	setup_idmap_userns
	setup_idmap_basic_mount

	update_config '.process.args = ["sh", "-c", "findmnt -o PROPAGATION /tmp/mount-1"]'
	# Add the shared option to the idmap mount.
	update_config '.mounts |= map((select(.source == "source-1/") | .options += ["shared"]) // .)'

	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"shared"* ]]
}

@test "idmap mount with relative path [userns]" {
	setup_idmap_userns
	setup_idmap_basic_mount

	update_config '.process.args = ["sh", "-c", "stat -c =%u=%g= /tmp/mount-1/foo.txt"]'
	# Switch the mount to have a relative mount destination.
	update_config '.mounts |= map((select(.source == "source-1/") | .destination = "tmp/mount-1") // .)'

	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=0=0="* ]]
}

@test "idmap mount with bind mount [userns]" {
	setup_idmap_userns
	setup_idmap_basic_mount
	setup_bind_mount

	update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/{,bind-}mount-1/foo.txt"]'

	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=/tmp/mount-1/foo.txt:0=0="* ]]
	[[ "$output" == *"=/tmp/bind-mount-1/foo.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
}

@test "idmap mount with bind mount [no userns]" {
	setup_idmap_basic_mount
	setup_bind_mount

	update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/{,bind-}mount-1/foo.txt"]'

	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=/tmp/mount-1/foo.txt:100000=100000="* ]]
	[[ "$output" == *"=/tmp/bind-mount-1/foo.txt:0=0="* ]]
}

@test "two idmap mounts (same mapping) with two bind mounts [userns]" {
	setup_idmap_userns

	setup_idmap_basic_mount 1
	setup_bind_mount 1
	setup_bind_mount 2
	setup_idmap_basic_mount 2

	update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-[12]/foo.txt"]'

	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=/tmp/mount-1/foo.txt:0=0="* ]]
	[[ "$output" == *"=/tmp/mount-2/foo.txt:1=1="* ]]
}

@test "same idmap mount (different mappings) [userns]" {
	setup_idmap_userns

	# Mount the same directory with different mappings. Make sure we also use
	# different mappings for uids and gids.
	setup_idmap_single_mount 100:100000:100 200:100000:100 multi1
	setup_idmap_single_mount 100:101000:100 200:102000:100 multi1 multi1-alt
	setup_idmap_single_mount 100:102000:100 200:103000:100 multi1-symlink multi1-alt-sym

	update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-multi1{,-alt{,-sym}}/{foo,bar,baz}.txt"]'

	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=/tmp/mount-multi1/foo.txt:0=11="* ]]
	[[ "$output" == *"=/tmp/mount-multi1/bar.txt:1=22="* ]]
	[[ "$output" == *"=/tmp/mount-multi1/baz.txt:2=33="* ]]
	[[ "$output" == *"=/tmp/mount-multi1-alt/foo.txt:1000=2011="* ]]
	[[ "$output" == *"=/tmp/mount-multi1-alt/bar.txt:1001=2022="* ]]
	[[ "$output" == *"=/tmp/mount-multi1-alt/baz.txt:1002=2033="* ]]
	[[ "$output" == *"=/tmp/mount-multi1-alt-sym/foo.txt:2000=3011="* ]]
	[[ "$output" == *"=/tmp/mount-multi1-alt-sym/bar.txt:2001=3022="* ]]
	[[ "$output" == *"=/tmp/mount-multi1-alt-sym/baz.txt:2002=3033="* ]]
}

@test "same idmap mount (different mappings) [no userns]" {
	# Mount the same directory with different mappings. Make sure we also use
	# different mappings for uids and gids.
	setup_idmap_single_mount 100:100000:100 200:100000:100 multi1
	setup_idmap_single_mount 100:101000:100 200:102000:100 multi1 multi1-alt
	setup_idmap_single_mount 100:102000:100 200:103000:100 multi1-symlink multi1-alt-sym

	update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-multi1{,-alt{,-sym}}/{foo,bar,baz}.txt"]'

	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=/tmp/mount-multi1/foo.txt:100000=100011="* ]]
	[[ "$output" == *"=/tmp/mount-multi1/bar.txt:100001=100022="* ]]
	[[ "$output" == *"=/tmp/mount-multi1/baz.txt:100002=100033="* ]]
	[[ "$output" == *"=/tmp/mount-multi1-alt/foo.txt:101000=102011="* ]]
	[[ "$output" == *"=/tmp/mount-multi1-alt/bar.txt:101001=102022="* ]]
	[[ "$output" == *"=/tmp/mount-multi1-alt/baz.txt:101002=102033="* ]]
	[[ "$output" == *"=/tmp/mount-multi1-alt-sym/foo.txt:102000=103011="* ]]
	[[ "$output" == *"=/tmp/mount-multi1-alt-sym/bar.txt:102001=103022="* ]]
	[[ "$output" == *"=/tmp/mount-multi1-alt-sym/baz.txt:102002=103033="* ]]
}

@test "multiple idmap mounts (different mappings) [userns]" {
	setup_idmap_userns

	# Make sure we use different mappings for uids and gids.
	setup_idmap_single_mount 100:101100:3 200:101900:50 multi1
	setup_idmap_single_mount 200:102200:3 200:102900:100 multi2
	setup_idmap_single_mount 5000000:103000:1000 6000000:103000:500 multi3

	update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-multi[123]/{foo,bar,baz}.txt"]'

	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=/tmp/mount-multi1/foo.txt:1100=1911="* ]]
	[[ "$output" == *"=/tmp/mount-multi1/bar.txt:1101=1922="* ]]
	[[ "$output" == *"=/tmp/mount-multi1/baz.txt:1102=1933="* ]]
	[[ "$output" == *"=/tmp/mount-multi2/foo.txt:2200=2911="* ]]
	[[ "$output" == *"=/tmp/mount-multi2/bar.txt:2201=2922="* ]]
	[[ "$output" == *"=/tmp/mount-multi2/baz.txt:2202=2933="* ]]
	[[ "$output" == *"=/tmp/mount-multi3/foo.txt:3528=3491="* ]]
	[[ "$output" == *"=/tmp/mount-multi3/bar.txt:3133=3337="* ]]
	[[ "$output" == *"=/tmp/mount-multi3/baz.txt:3999=3444="* ]]
}

@test "multiple idmap mounts (different mappings) [no userns]" {
	# Make sure we use different mappings for uids and gids.
	setup_idmap_single_mount 100:1100:3 200:1900:50 multi1
	setup_idmap_single_mount 200:2200:3 200:2900:100 multi2
	setup_idmap_single_mount 5000000:3000:1000 6000000:3000:500 multi3

	update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-multi[123]/{foo,bar,baz}.txt"]'

	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=/tmp/mount-multi1/foo.txt:1100=1911="* ]]
	[[ "$output" == *"=/tmp/mount-multi1/bar.txt:1101=1922="* ]]
	[[ "$output" == *"=/tmp/mount-multi1/baz.txt:1102=1933="* ]]
	[[ "$output" == *"=/tmp/mount-multi2/foo.txt:2200=2911="* ]]
	[[ "$output" == *"=/tmp/mount-multi2/bar.txt:2201=2922="* ]]
	[[ "$output" == *"=/tmp/mount-multi2/baz.txt:2202=2933="* ]]
	[[ "$output" == *"=/tmp/mount-multi3/foo.txt:3528=3491="* ]]
	[[ "$output" == *"=/tmp/mount-multi3/bar.txt:3133=3337="* ]]
	[[ "$output" == *"=/tmp/mount-multi3/baz.txt:3999=3444="* ]]
}

@test "idmap mount (complicated mapping) [userns]" {
	setup_idmap_userns

	update_config '.mounts += [
			{
				"source": "source-multi1/",
				"destination": "/tmp/mount-multi1",
				"options": ["bind"],
				"uidMappings": [
					{"containerID": 100, "hostID": 101000, "size": 1},
					{"containerID": 101, "hostID": 102000, "size": 1},
					{"containerID": 102, "hostID": 103000, "size": 1}
				],
				"gidMappings": [
					{"containerID": 210, "hostID": 101100, "size": 10},
					{"containerID": 220, "hostID": 102200, "size": 10},
					{"containerID": 230, "hostID": 103300, "size": 10}
				]
			}
		]'

	update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-multi1/{foo,bar,baz}.txt"]'
	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=/tmp/mount-multi1/foo.txt:1000=1101="* ]]
	[[ "$output" == *"=/tmp/mount-multi1/bar.txt:2000=2202="* ]]
	[[ "$output" == *"=/tmp/mount-multi1/baz.txt:3000=3303="* ]]
}

@test "idmap mount (complicated mapping) [no userns]" {
	update_config '.mounts += [
			{
				"source": "source-multi1/",
				"destination": "/tmp/mount-multi1",
				"options": ["bind"],
				"uidMappings": [
					{"containerID": 100, "hostID": 1000, "size": 1},
					{"containerID": 101, "hostID": 2000, "size": 1},
					{"containerID": 102, "hostID": 3000, "size": 1}
				],
				"gidMappings": [
					{"containerID": 210, "hostID": 1100, "size": 10},
					{"containerID": 220, "hostID": 2200, "size": 10},
					{"containerID": 230, "hostID": 3300, "size": 10}
				]
			}
		]'

	update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-multi1/{foo,bar,baz}.txt"]'
	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=/tmp/mount-multi1/foo.txt:1000=1101="* ]]
	[[ "$output" == *"=/tmp/mount-multi1/bar.txt:2000=2202="* ]]
	[[ "$output" == *"=/tmp/mount-multi1/baz.txt:3000=3303="* ]]
}

@test "idmap mount (non-recursive idmap) [userns]" {
	setup_idmap_userns

	setup_host_bind_mount "source-multi1/" "mnt-subtree/multi1"
	setup_host_bind_mount "source-multi2/" "mnt-subtree/multi2"

	update_config '.mounts += [
			{
				"source": "mnt-subtree/",
				"destination": "/tmp/mount-tree",
				"options": ["rbind"],
				"uidMappings": [
					{"containerID": 100, "hostID": 101000, "size": 3},
					{"containerID": 200, "hostID": 102000, "size": 3},
					{"containerID": 300, "hostID": 103000, "size": 3}
				],
				"gidMappings": [
					{"containerID": 210, "hostID": 101100, "size": 10},
					{"containerID": 220, "hostID": 102200, "size": 10},
					{"containerID": 230, "hostID": 103300, "size": 10}
				]
			}
		]'

	update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-tree{,/multi1,/multi2}/{foo,bar,baz}.txt"]'
	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=/tmp/mount-tree/foo.txt:1000=1101="* ]]
	[[ "$output" == *"=/tmp/mount-tree/bar.txt:2000=2202="* ]]
	[[ "$output" == *"=/tmp/mount-tree/baz.txt:3000=3303="* ]]
	# Because we used "idmap", the child mounts were not remapped recursively.
	[[ "$output" == *"=/tmp/mount-tree/multi1/foo.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi1/bar.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi1/baz.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/foo.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/bar.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/baz.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
}

@test "idmap mount (non-recursive idmap) [no userns]" {
	setup_host_bind_mount "source-multi1/" "mnt-subtree/multi1"
	setup_host_bind_mount "source-multi2/" "mnt-subtree/multi2"

	update_config '.mounts += [
			{
				"source": "mnt-subtree/",
				"destination": "/tmp/mount-tree",
				"options": ["rbind"],
				"uidMappings": [
					{"containerID": 100, "hostID": 101000, "size": 3},
					{"containerID": 200, "hostID": 102000, "size": 3},
					{"containerID": 300, "hostID": 103000, "size": 3}
				],
				"gidMappings": [
					{"containerID": 210, "hostID": 101100, "size": 10},
					{"containerID": 220, "hostID": 102200, "size": 10},
					{"containerID": 230, "hostID": 103300, "size": 10}
				]
			}
		]'

	update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-tree{,/multi1,/multi2}/{foo,bar,baz}.txt"]'
	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=/tmp/mount-tree/foo.txt:101000=101101="* ]]
	[[ "$output" == *"=/tmp/mount-tree/bar.txt:102000=102202="* ]]
	[[ "$output" == *"=/tmp/mount-tree/baz.txt:103000=103303="* ]]
	# Because we used "idmap", the child mounts were not remapped recursively.
	[[ "$output" == *"=/tmp/mount-tree/multi1/foo.txt:100=211="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi1/bar.txt:101=222="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi1/baz.txt:102=233="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/foo.txt:200=211="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/bar.txt:201=222="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/baz.txt:202=233="* ]]
}

@test "idmap mount (idmap flag) [userns]" {
	setup_idmap_userns

	setup_host_bind_mount "source-multi1/" "mnt-subtree/multi1"
	setup_host_bind_mount "source-multi2/" "mnt-subtree/multi2"

	update_config '.mounts += [
			{
				"source": "mnt-subtree/",
				"destination": "/tmp/mount-tree",
				"options": ["rbind", "idmap"],
				"uidMappings": [
					{"containerID": 100, "hostID": 101000, "size": 3},
					{"containerID": 200, "hostID": 102000, "size": 3},
					{"containerID": 300, "hostID": 103000, "size": 3}
				],
				"gidMappings": [
					{"containerID": 210, "hostID": 101100, "size": 10},
					{"containerID": 220, "hostID": 102200, "size": 10},
					{"containerID": 230, "hostID": 103300, "size": 10}
				]
			}
		]'

	update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-tree{,/multi1,/multi2}/{foo,bar,baz}.txt"]'
	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=/tmp/mount-tree/foo.txt:1000=1101="* ]]
	[[ "$output" == *"=/tmp/mount-tree/bar.txt:2000=2202="* ]]
	[[ "$output" == *"=/tmp/mount-tree/baz.txt:3000=3303="* ]]
	# Because we used "idmap", the child mounts were not remapped recursively.
	[[ "$output" == *"=/tmp/mount-tree/multi1/foo.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi1/bar.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi1/baz.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/foo.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/bar.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/baz.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
}

@test "idmap mount (idmap flag) [no userns]" {
	setup_host_bind_mount "source-multi1/" "mnt-subtree/multi1"
	setup_host_bind_mount "source-multi2/" "mnt-subtree/multi2"

	update_config '.mounts += [
			{
				"source": "mnt-subtree/",
				"destination": "/tmp/mount-tree",
				"options": ["rbind", "idmap"],
				"uidMappings": [
					{"containerID": 100, "hostID": 101000, "size": 3},
					{"containerID": 200, "hostID": 102000, "size": 3},
					{"containerID": 300, "hostID": 103000, "size": 3}
				],
				"gidMappings": [
					{"containerID": 210, "hostID": 101100, "size": 10},
					{"containerID": 220, "hostID": 102200, "size": 10},
					{"containerID": 230, "hostID": 103300, "size": 10}
				]
			}
		]'

	update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-tree{,/multi1,/multi2}/{foo,bar,baz}.txt"]'
	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=/tmp/mount-tree/foo.txt:101000=101101="* ]]
	[[ "$output" == *"=/tmp/mount-tree/bar.txt:102000=102202="* ]]
	[[ "$output" == *"=/tmp/mount-tree/baz.txt:103000=103303="* ]]
	# Because we used "idmap", the child mounts were not remapped recursively.
	[[ "$output" == *"=/tmp/mount-tree/multi1/foo.txt:100=211="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi1/bar.txt:101=222="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi1/baz.txt:102=233="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/foo.txt:200=211="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/bar.txt:201=222="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/baz.txt:202=233="* ]]
}

@test "idmap mount (ridmap flag) [userns]" {
	setup_idmap_userns

	setup_host_bind_mount "source-multi1/" "mnt-subtree/multi1"
	setup_host_bind_mount "source-multi2/" "mnt-subtree/multi2"

	update_config '.mounts += [
			{
				"source": "mnt-subtree/",
				"destination": "/tmp/mount-tree",
				"options": ["rbind", "ridmap"],
				"uidMappings": [
					{"containerID": 100, "hostID": 101000, "size": 3},
					{"containerID": 200, "hostID": 102000, "size": 3},
					{"containerID": 300, "hostID": 103000, "size": 3}
				],
				"gidMappings": [
					{"containerID": 210, "hostID": 101100, "size": 10},
					{"containerID": 220, "hostID": 102200, "size": 10},
					{"containerID": 230, "hostID": 103300, "size": 10}
				]
			}
		]'

	update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-tree{,/multi1,/multi2}/{foo,bar,baz}.txt"]'
	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=/tmp/mount-tree/foo.txt:1000=1101="* ]]
	[[ "$output" == *"=/tmp/mount-tree/bar.txt:2000=2202="* ]]
	[[ "$output" == *"=/tmp/mount-tree/baz.txt:3000=3303="* ]]
	# The child mounts have the same mapping applied.
	[[ "$output" == *"=/tmp/mount-tree/multi1/foo.txt:1000=1101="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi1/bar.txt:1001=2202="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi1/baz.txt:1002=3303="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/foo.txt:2000=1101="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/bar.txt:2001=2202="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/baz.txt:2002=3303="* ]]
}

@test "idmap mount (ridmap flag) [no userns]" {
	setup_host_bind_mount "source-multi1/" "mnt-subtree/multi1"
	setup_host_bind_mount "source-multi2/" "mnt-subtree/multi2"

	update_config '.mounts += [
			{
				"source": "mnt-subtree/",
				"destination": "/tmp/mount-tree",
				"options": ["rbind", "ridmap"],
				"uidMappings": [
					{"containerID": 100, "hostID": 101000, "size": 3},
					{"containerID": 200, "hostID": 102000, "size": 3},
					{"containerID": 300, "hostID": 103000, "size": 3}
				],
				"gidMappings": [
					{"containerID": 210, "hostID": 101100, "size": 10},
					{"containerID": 220, "hostID": 102200, "size": 10},
					{"containerID": 230, "hostID": 103300, "size": 10}
				]
			}
		]'

	update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-tree{,/multi1,/multi2}/{foo,bar,baz}.txt"]'
	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=/tmp/mount-tree/foo.txt:101000=101101="* ]]
	[[ "$output" == *"=/tmp/mount-tree/bar.txt:102000=102202="* ]]
	[[ "$output" == *"=/tmp/mount-tree/baz.txt:103000=103303="* ]]
	# The child mounts have the same mapping applied.
	[[ "$output" == *"=/tmp/mount-tree/multi1/foo.txt:101000=101101="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi1/bar.txt:101001=102202="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi1/baz.txt:101002=103303="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/foo.txt:102000=101101="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/bar.txt:102001=102202="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/baz.txt:102002=103303="* ]]
}

@test "idmap mount (idmap flag, implied mapping) [userns]" {
	setup_idmap_userns

	setup_host_bind_mount "source-multi1/" "mnt-subtree/multi1"
	setup_host_bind_mount "source-multi2/" "mnt-subtree/multi2"

	update_config '.mounts += [
			{
				"source": "mnt-subtree/",
				"destination": "/tmp/mount-tree",
				"options": ["rbind", "idmap"],
			}
		]'

	update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-tree{,/multi1,/multi2}/{foo,bar,baz}.txt"]'
	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=/tmp/mount-tree/foo.txt:100=211="* ]]
	[[ "$output" == *"=/tmp/mount-tree/bar.txt:200=222="* ]]
	[[ "$output" == *"=/tmp/mount-tree/baz.txt:300=233="* ]]
	# Because we used "idmap", the child mounts were not remapped recursively.
	[[ "$output" == *"=/tmp/mount-tree/multi1/foo.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi1/bar.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi1/baz.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/foo.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/bar.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/baz.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
}

@test "idmap mount (ridmap flag, implied mapping) [userns]" {
	setup_idmap_userns

	setup_host_bind_mount "source-multi1/" "mnt-subtree/multi1"
	setup_host_bind_mount "source-multi2/" "mnt-subtree/multi2"

	update_config '.mounts += [
			{
				"source": "mnt-subtree/",
				"destination": "/tmp/mount-tree",
				"options": ["rbind", "ridmap"],
			}
		]'

	update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-tree{,/multi1,/multi2}/{foo,bar,baz}.txt"]'
	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=/tmp/mount-tree/foo.txt:100=211="* ]]
	[[ "$output" == *"=/tmp/mount-tree/bar.txt:200=222="* ]]
	[[ "$output" == *"=/tmp/mount-tree/baz.txt:300=233="* ]]
	# The child mounts have the same mapping applied.
	[[ "$output" == *"=/tmp/mount-tree/multi1/foo.txt:100=211="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi1/bar.txt:101=222="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi1/baz.txt:102=233="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/foo.txt:200=211="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/bar.txt:201=222="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/baz.txt:202=233="* ]]
}

@test "idmap mount (idmap flag, implied mapping, userns join) [userns]" {
	# Create a detached container with the id-mapping we want.
	cp config.json config.json.bak
	update_config '.linux.namespaces += [{"type": "user"}]
		| .linux.uidMappings += [{"containerID": 0, "hostID": 100000, "size": 65536}]
		| .linux.gidMappings += [{"containerID": 0, "hostID": 100000, "size": 65536}]'
	update_config '.process.args = ["sleep", "infinity"]'

	runc run -d --console-socket "$CONSOLE_SOCKET" target_userns
	[ "$status" -eq 0 ]

	# Configure our container to attach to the first container's userns.
	target_pid="$(__runc state target_userns | jq .pid)"
	update_config '.linux.namespaces |= map(if .type == "user" then (.path = "/proc/'"$target_pid"'/ns/" + .type) else . end)'
	update_config 'del(.linux.uidMappings) | del(.linux.gidMappings)'

	setup_host_bind_mount "source-multi1/" "mnt-subtree/multi1"
	setup_host_bind_mount "source-multi2/" "mnt-subtree/multi2"

	update_config '.mounts += [
			{
				"source": "mnt-subtree/",
				"destination": "/tmp/mount-tree",
				"options": ["rbind", "idmap"],
			}
		]'

	update_config '.process.args = ["bash", "-c", "stat -c =%n:%u=%g= /tmp/mount-tree{,/multi1,/multi2}/{foo,bar,baz}.txt"]'
	runc run test_debian
	[ "$status" -eq 0 ]
	[[ "$output" == *"=/tmp/mount-tree/foo.txt:100=211="* ]]
	[[ "$output" == *"=/tmp/mount-tree/bar.txt:200=222="* ]]
	[[ "$output" == *"=/tmp/mount-tree/baz.txt:300=233="* ]]
	# Because we used "idmap", the child mounts were not remapped recursively.
	[[ "$output" == *"=/tmp/mount-tree/multi1/foo.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi1/bar.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi1/baz.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/foo.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/bar.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
	[[ "$output" == *"=/tmp/mount-tree/multi2/baz.txt:$OVERFLOW_UID=$OVERFLOW_GID="* ]]
}
