fix(installer): identify live loop backing after dracut switch-root
This commit is contained in:
+31
-11
@@ -10,6 +10,7 @@ from __future__ import annotations
|
||||
import argparse
|
||||
from dataclasses import dataclass
|
||||
import getpass
|
||||
import fcntl
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path, PurePosixPath
|
||||
@@ -17,6 +18,7 @@ import re
|
||||
import shlex
|
||||
import shutil
|
||||
import stat
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
@@ -241,17 +243,12 @@ class SystemProbe:
|
||||
# Mounted loops may hide the physical filesystem that stores their file.
|
||||
for path, node in topo.nodes.items():
|
||||
if node['type'] == 'loop' and path in topo.protected:
|
||||
backing_file = Path('/sys/dev/block') / node['maj:min'] / 'loop/backing_file'
|
||||
try:
|
||||
backing = backing_file.read_text().strip()
|
||||
except OSError as exc:
|
||||
raise InstallError(f'Cannot inspect mounted loop backing file: {path}') from exc
|
||||
backing = re.sub(r'\\([0-7]{3})', lambda m: chr(int(m[1], 8)), backing)
|
||||
if not backing.startswith('/'):
|
||||
backing = '/' + backing
|
||||
source = self._source_for_path(backing, topo)
|
||||
if not source:
|
||||
raise InstallError(f'Unknown physical backing for mounted loop: {path}')
|
||||
identity = self._loop_backing_identity(path, node['maj:min'])
|
||||
sources = [p for p, n in topo.nodes.items() if n['maj:min'] == identity]
|
||||
if len(sources) != 1:
|
||||
raise InstallError(f'Unknown physical backing for mounted loop: {path} ({identity})')
|
||||
source = sources[0]
|
||||
topo.protect(source)
|
||||
topo.link(path, source)
|
||||
if not Path(payload).is_file():
|
||||
raise InstallError(f'Prepared squashfs payload is not a regular file: {payload}')
|
||||
@@ -262,6 +259,29 @@ class SystemProbe:
|
||||
topo.protect(source)
|
||||
return topo
|
||||
|
||||
@staticmethod
|
||||
def _loop_backing_identity(path, expected_identity):
|
||||
# LOOP_GET_STATUS64 reports the backing inode's filesystem device,
|
||||
# independent of names which dracut switch_root may have relocated.
|
||||
# lo_rdevice is nonzero when the backing inode is itself a block device.
|
||||
try:
|
||||
descriptor = os.open(path, os.O_RDONLY | os.O_CLOEXEC)
|
||||
try:
|
||||
info = os.fstat(descriptor)
|
||||
identity = f'{os.major(info.st_rdev)}:{os.minor(info.st_rdev)}'
|
||||
if not stat.S_ISBLK(info.st_mode) or identity != expected_identity:
|
||||
raise InstallError(f'Mounted loop device identity changed: {path}')
|
||||
status = fcntl.ioctl(descriptor, 0x4C05, bytes(232))
|
||||
if not isinstance(status, bytes) or len(status) != 232:
|
||||
raise InstallError(f'Malformed loop status: {path}')
|
||||
filesystem_device, _, block_device = struct.unpack_from('=QQQ', status)
|
||||
finally:
|
||||
os.close(descriptor)
|
||||
except (OSError, struct.error) as exc:
|
||||
raise InstallError(f'Cannot inspect mounted loop backing identity: {path}') from exc
|
||||
device = block_device or filesystem_device
|
||||
return f'{os.major(device)}:{os.minor(device)}'
|
||||
|
||||
@staticmethod
|
||||
def _mounts(raw):
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user