From 916374bf9e712166d4c1aff65caf078dd9ab5e9e Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Wed, 16 Mar 2016 21:32:53 +0100 Subject: [PATCH] tests/integration: use ctypes for mount instead of util-linux Some versions of util-linux had a bug with respect to private/slave mounts. The kernel doesn't expect the option to be passed during the initial mount but only during subsequent calls (you mount the FS, you make it private and slave). Directly use mount(2) to avoid the problem. --- tests/integration/fixtures/programs.py | 56 ++++++++++++++++++-------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/tests/integration/fixtures/programs.py b/tests/integration/fixtures/programs.py index 7c26710d..46afe27a 100644 --- a/tests/integration/fixtures/programs.py +++ b/tests/integration/fixtures/programs.py @@ -10,8 +10,38 @@ import multiprocessing import uuid import time import platform +import ctypes from collections import namedtuple +libc = ctypes.CDLL('libc.so.6', use_errno=True) + + +def mount_bind(source, target): + ret = libc.mount(source.encode('ascii'), + target.encode('ascii'), + None, + 4096, # MS_BIND + None) + if ret == -1: + e = ctypes.get_errno() + raise OSError(e, os.strerror(e)) + + +def mount_tmpfs(target, private=False): + flags = [0] + if private: + flags.append(1 << 18) # MS_PRIVATE + flags.append(1 << 19) # MS_SLAVE + for fl in flags: + ret = libc.mount(b"none", + target.encode('ascii'), + b"tmpfs", + fl, + None) + if ret == -1: + e = ctypes.get_errno() + raise OSError(e, os.strerror(e)) + def most_recent(*args): """Return the most recent files matching one of the provided glob @@ -33,9 +63,8 @@ def _replace_file(tmpdir, target, content): tmpname = str(uuid.uuid1()) with tmpdir.join(tmpname).open("w") as tmp: tmp.write(content) - subprocess.check_call(["mount", "-n", "--bind", - str(tmpdir.join(tmpname)), - target]) + mount_bind(str(tmpdir.join(tmpname)), + target) @pytest.fixture @@ -128,15 +157,11 @@ class LldpdFactory(object): # Chroot chroot = self.config.lldpd.privsep.chroot if os.path.isdir(chroot): - subprocess.check_call(["mount", "-n", - "-t", "tmpfs", - "tmpfs", chroot]) + mount_tmpfs(chroot) else: parent = os.path.abspath(os.path.join(chroot, os.pardir)) assert os.path.isdir(parent) - subprocess.check_call(["mount", "-n", - "-t", "tmpfs", - "tmpfs", parent]) + mount_tmpfs(parent) # User/group user = self.config.lldpd.privsep.user group = self.config.lldpd.privsep.group @@ -160,13 +185,11 @@ class LldpdFactory(object): # Also setup the "namespace-dependant" directory tmpdir.join("ns").ensure(dir=True) - subprocess.check_call(["mount", "-n", "--make-slave", "--make-private", - "-t", "tmpfs", - "tmpfs", str(tmpdir.join("ns"))]) + mount_tmpfs(str(tmpdir.join("ns")), private=True) # We also need a proper /etc/os-release _replace_file(tmpdir, "/etc/os-release", - """PRETTY_NAME="Spectacular GNU/Linux 2016" + """PRETTY_NAME="Spectacular GNU/Linux 2016" NAME="Spectacular GNU/Linux" ID=spectacular HOME_URL="https://www.example.com/spectacular" @@ -179,13 +202,13 @@ BUG_REPORT_URL="https://www.example.com/spectacular/bugs" # And we need to ensure name resolution is sane _replace_file(tmpdir, "/etc/hosts", - """ + """ 127.0.0.1 localhost.localdomain localhost 127.0.1.1 {name}.example.com {name} ::1 ip6-localhost ip6-loopback """.format(name=name)) _replace_file(tmpdir, "/etc/nsswitch.conf", - """ + """ passwd: files group: files shadow: files @@ -201,8 +224,7 @@ services: files _replace_file(tmpdir, path, "") path = os.path.join(self.config.lldpd.confdir, "lldpd.d") if os.path.isdir(path): - subprocess.check_call(["mount", "-n", "-t", "tmpfs", - "tmpfs", path]) + mount_tmpfs(path) @pytest.fixture() -- 2.39.5