Skip to content

Commit

Permalink
storaged: Use a private mount for "btrfs-tool do"
Browse files Browse the repository at this point in the history
This should prevent other parts of the system noticing this mount and
keeping it busy or being able to read data from it.

https://issues.redhat.com/browse/COCKPIT-1210

Fixes #21355
  • Loading branch information
mvollmer committed Dec 5, 2024
1 parent 8b49506 commit 2568fb6
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 26 deletions.
54 changes: 31 additions & 23 deletions pkg/storaged/btrfs/btrfs-tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# wont get confused.

import contextlib
import ctypes
import fcntl
import json
import os
Expand Down Expand Up @@ -67,19 +68,6 @@ def mount_database():
os.close(fd)


# There is contextlib.chdir in Python 3.11, which we should use once
# it is available everywhere.
#
@contextlib.contextmanager
def context_chdir(path):
old_cwd = os.getcwd()
os.chdir(path)
try:
yield
finally:
os.chdir(old_cwd)


def list_filesystems():
output = json.loads(subprocess.check_output(["lsblk", "--json", "--paths", "--list", "--noheadings",
"--output", "NAME,FSTYPE,UUID,MOUNTPOINTS"]))
Expand Down Expand Up @@ -143,9 +131,10 @@ def remove_tmp_mountpoint(db, uuid):


def remove_all_tmp_mountpoints():
with mount_database() as db:
for mp in set(tmp_mountpoints):
remove_tmp_mountpoint(db, mp)
if len(tmp_mountpoints) > 0:
with mount_database() as db:
for mp in set(tmp_mountpoints):
remove_tmp_mountpoint(db, mp)


def force_mount_point(db, fs, opt_repair):
Expand Down Expand Up @@ -241,15 +230,34 @@ def cmd_poll(opt_mount):
sys.stdout.flush()


def unshare_mounts():
# The os.unshare function is available since Python 3.12, but we
# still need to support older Pythons.
if "unshare" in os.__dict__:
os.unshare(os.CLONE_NEWNS)
else:
libc = ctypes.CDLL(None)
libc.unshare.argtypes = [ctypes.c_int]
get_errno_loc = libc.__errno_location
get_errno_loc.restype = ctypes.POINTER(ctypes.c_int)
ret = libc.unshare(2**17)
if ret != 0:
errno = get_errno_loc()[0]
raise OSError(errno, os.strerror(errno))


def cmd_do(uuid, cmd):
debug(f"DO {uuid} {cmd}")
with mount_database() as db:
filesystems = list_filesystems()
for fs in filesystems.values():
if fs['uuid'] == uuid:
mp = force_mount_point(db, fs, opt_repair=True)
with context_chdir(mp):
subprocess.check_call(cmd)
filesystems = list_filesystems()
for fs in filesystems.values():
if fs['uuid'] == uuid:
path = "/run/cockpit/btrfs"
dev = fs['devices'][0]
os.makedirs(path, mode=0o700, exist_ok=True)
unshare_mounts()
subprocess.check_call(["mount", "--make-rprivate", "/"])
subprocess.check_call(["mount", dev, path])
subprocess.check_call(cmd, cwd=path)


def cmd(args):
Expand Down
8 changes: 5 additions & 3 deletions test/verify/check-storage-btrfs
Original file line number Diff line number Diff line change
Expand Up @@ -554,13 +554,15 @@ class TestStorageBtrfs(storagelib.StorageCase):
# creation of btrfs partition can take a while on TF.
with b.wait_timeout(30):
b.wait_visible(self.card_row("Storage", name="sda"))

# Make sure Cockpit hasn't mounted it in a secret
# place. This should only happen in Anaconda mode.
self.assertNotIn("/dev/mapper/btrfs-test", m.execute("findmnt"))

b.wait_in_text(self.card_row("Storage", name="sda"), "btrfs filesystem (encrypted)")
self.click_dropdown(self.card_row("Storage", name="sda") + " + tr", "Mount")
self.dialog({"mount_point": mount_point})

# Wait for Cockpit's own mount to go away
b.wait(lambda: "/var/lib/cockpit/btrfs" not in m.execute("findmnt"))

m.execute(f"""
umount {mount_point}
cryptsetup luksClose /dev/mapper/btrfs-test
Expand Down

0 comments on commit 2568fb6

Please sign in to comment.