Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various fstab support fixes, part 2 #1343

Merged
merged 5 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion blivet/devices/btrfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,6 @@ class BTRFSSubVolumeDevice(BTRFSDevice):

""" A btrfs subvolume pseudo-device. """
_type = "btrfs subvolume"
_format_immutable = True

def __init__(self, *args, **kwargs):
"""
Expand All @@ -567,6 +566,10 @@ def __init__(self, *args, **kwargs):

self.volume._add_subvolume(self)

@property
def format_immutable(self):
return super(BTRFSSubVolumeDevice, self).format_immutable or self.exists

def _set_mountopts(self):
# propagate mount options specified for members via kickstart
opts = "subvol=%s" % self.name
Expand Down
8 changes: 5 additions & 3 deletions blivet/formats/fs.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
from . import DeviceFormat, register_device_format
from .. import util
from ..flags import flags
from ..fstab import FSTabOptions, HAVE_LIBMOUNT
from ..fstab import HAVE_LIBMOUNT
from ..storage_log import log_exception_info, log_method_call
from .. import arch
from ..size import Size, ROUND_UP
Expand Down Expand Up @@ -132,8 +132,6 @@ def __init__(self, **kwargs):

DeviceFormat.__init__(self, **kwargs)

self.fstab = FSTabOptions()

# Create task objects
self._fsck = self._fsck_class(self)
self._mkfs = self._mkfs_class(self)
Expand All @@ -148,6 +146,10 @@ def __init__(self, **kwargs):

self.mountpoint = kwargs.get("mountpoint")
self.mountopts = kwargs.get("mountopts", "")
self.freq = kwargs.get("freq", 0)
self.passno = kwargs.get("passno", 0)
self.fstab_spec_type = kwargs.get("fstab_spec_type", None)

self.label = kwargs.get("label")
self.fsprofile = kwargs.get("fsprofile")
self._mkfs_nodiscard = kwargs.get("nodiscard", False)
Expand Down
7 changes: 4 additions & 3 deletions blivet/formats/swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@

from parted import PARTITION_SWAP, fileSystemType
from ..errors import FSWriteUUIDError, SwapSpaceError
from ..fstab import FSTabOptions
from ..storage_log import log_method_call
from ..tasks import availability
from . import DeviceFormat, register_device_format
Expand Down Expand Up @@ -78,11 +77,13 @@ def __init__(self, **kwargs):
log_method_call(self, **kwargs)
DeviceFormat.__init__(self, **kwargs)

self.fstab = FSTabOptions()

self.priority = kwargs.get("priority", -1)
self.label = kwargs.get("label")

self.freq = kwargs.get("freq", 0)
self.passno = kwargs.get("passno", 0)
self.fstab_spec_type = kwargs.get("fstab_spec_type", None)

def __repr__(self):
s = DeviceFormat.__repr__(self)
s += (" priority = %(priority)s label = %(label)s" %
Expand Down
113 changes: 48 additions & 65 deletions blivet/fstab.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,28 +34,11 @@
log = logging.getLogger("blivet")


class FSTabOptions(object):
""" User preferred fstab settings object intended to be attached to device.format.
Set variables override otherwise automatically obtained values put into fstab.
"""

def __init__(self):
self.freq = None
self.passno = None

# preferred spec identification type; default "UUID"
# possible values: None, "UUID", "LABEL", "PARTLABEL", "PARTUUID", "PATH"
self.spec_type = None

# list of fstab options to be used
self.mntops = []


class FSTabEntry(object):
""" One processed line of fstab
"""

def __init__(self, spec=None, file=None, vfstype=None, mntops=None,
def __init__(self, spec=None, file=None, vfstype=None, mntopts=None,
freq=None, passno=None, comment=None, *, entry=None):

# Note: "*" in arguments means that every following parameter can be used
Expand All @@ -72,8 +55,8 @@ def __init__(self, spec=None, file=None, vfstype=None, mntops=None,
self.file = file
if vfstype is not None:
self.vfstype = vfstype
if mntops is not None:
self.mntops = mntops
if mntopts is not None:
self.mntopts = mntopts
if freq is not None:
self.freq = freq
if passno is not None:
Expand Down Expand Up @@ -169,7 +152,7 @@ def vfstype(self, value):
self._entry.fstype = value if value is not None else ""

@property
def mntops(self):
def mntopts(self):
""" Return mount options

:returns: list of mount options or None when not set
Expand All @@ -181,7 +164,7 @@ def mntops(self):

return self._entry.options.split(',')

def get_raw_mntops(self):
def get_raw_mntopts(self):
""" Return mount options

:returns: comma separated string of mount options or None when not set
Expand All @@ -190,11 +173,11 @@ def get_raw_mntops(self):

return self._entry.options if self._entry.options != "" else None

@mntops.setter
def mntops(self, values):
@mntopts.setter
def mntopts(self, values):
""" Set new mount options from the list of strings

:param values: mount options (see man fstab(5) fs_mntops)
:param values: mount options (see man fstab(5) fs_mntopts)
:type values: list of str
"""

Expand All @@ -206,10 +189,10 @@ def mntops(self, values):
else:
self._entry.options = ','.join([x for x in values if x != ""])

def mntops_add(self, values):
def mntopts_add(self, values):
""" Append new mount options to already existing ones

:param values: mount options (see man fstab(5) fs_mntops)
:param values: mount options (see man fstab(5) fs_mntopts)
:type values: list of str
"""

Expand Down Expand Up @@ -255,7 +238,7 @@ def is_valid(self):
:returns: False if any of the listed values is not set; otherwise True
:rtype: bool
"""
items = [self.spec, self.file, self.vfstype, self.mntops, self.freq, self.passno]
items = [self.spec, self.file, self.vfstype, self.mntopts, self.freq, self.passno]

# (Property getters replace empty strings with None)
return not any(x is None for x in items)
Expand Down Expand Up @@ -409,7 +392,7 @@ def entry_from_device(self, device):
else:
entry.vfstype = device.format.type

entry.mntops = device.format.options
entry.mntopts = device.format.options

if device.format.check and entry.file == "/":
entry.passno = 1
Expand Down Expand Up @@ -460,7 +443,7 @@ def entry_from_action(self, action):
else:
entry.vfstype = action.device.format.type

entry.mntops = action.device.format.options
entry.mntopts = action.device.format.options

if action.device.format.check and entry.file == "/":
entry.passno = 1
Expand Down Expand Up @@ -491,34 +474,34 @@ def read(self):

self._table.parse_fstab(self.src_file)

def find_device(self, devicetree, spec=None, mntops=None, blkid_tab=None, crypt_tab=None, *, entry=None):
def find_device(self, devicetree, spec=None, mntopts=None, blkid_tab=None, crypt_tab=None, *, entry=None):
""" Find a blivet device, based on spec or entry. Mount options can be used to refine the search.
If both entry and spec/mntops are given, spec/mntops are prioritized over entry values.
If both entry and spec/mntopts are given, spec/mntopts are prioritized over entry values.

:param devicetree: populated blivet.Devicetree instance
:type devicetree: :class: `blivet.Devicetree`
:keyword spec: searched device specs (see man fstab(5) fs_spec)
:type spec: str
:keyword mntops: list of mount option strings (see man fstab(5) fs_mntops)
:keyword mntopts: list of mount option strings (see man fstab(5) fs_mntopts)
:type mnops: list
:keyword blkid_tab: Blkidtab object
:type blkid_tab: :class: `BlkidTab`
:keyword crypt_tab: Crypttab object
:type crypt_tab: :class: `CryptTab`
:keyword entry: fstab entry with its spec (and mntops) filled as an alternative input type
:keyword entry: fstab entry with its spec (and mntopts) filled as an alternative input type
:type: :class: `FSTabEntry`
:returns: found device or None
:rtype: :class: `~.devices.StorageDevice` or None
"""

_spec = spec or (entry.spec if entry is not None else None)
_mntops = mntops or (entry.mntops if entry is not None else None)
_mntops_str = ",".join(_mntops) if mntops is not None else None
_mntopts = mntopts or (entry.mntopts if entry is not None else None)
_mntopts_str = ",".join(_mntopts) if mntopts is not None else None

return devicetree.resolve_device(_spec, options=_mntops_str, blkid_tab=blkid_tab, crypt_tab=crypt_tab)
return devicetree.resolve_device(_spec, options=_mntopts_str, blkid_tab=blkid_tab, crypt_tab=crypt_tab)

def get_device(self, devicetree, spec=None, file=None, vfstype=None,
mntops=None, blkid_tab=None, crypt_tab=None, *, entry=None):
mntopts=None, blkid_tab=None, crypt_tab=None, *, entry=None):
""" Parse an fstab entry for a device and return the corresponding device from the devicetree.
If not found, try to create a new device based on given values.
Raises UnrecognizedFSTabError in case of invalid or incomplete data.
Expand All @@ -527,7 +510,7 @@ def get_device(self, devicetree, spec=None, file=None, vfstype=None,
:type devicetree: :class: `blivet.Devicetree`
:keyword spec: searched device specs (see man fstab(5) fs_spec)
:type spec: str
:keyword mntops: list of mount option strings (see man fstab(5) fs_mntops)
:keyword mntopts: list of mount option strings (see man fstab(5) fs_mntopts)
:type mnops: list
:keyword blkid_tab: Blkidtab object
:type blkid_tab: :class: `BlkidTab`
Expand All @@ -544,11 +527,11 @@ def get_device(self, devicetree, spec=None, file=None, vfstype=None,
from blivet.errors import UnrecognizedFSTabEntryError

_spec = spec or (entry.spec if entry is not None else None)
_mntops = mntops or (entry.mntops if entry is not None else None)
_mntops_str = ",".join(_mntops) if mntops is not None else None
_mntopts = mntopts or (entry.mntopts if entry is not None else None)
_mntopts_str = ",".join(_mntopts) if mntopts is not None else None

# find device in the tree
device = devicetree.resolve_device(_spec, options=_mntops_str, blkid_tab=blkid_tab, crypt_tab=crypt_tab)
device = devicetree.resolve_device(_spec, options=_mntopts_str, blkid_tab=blkid_tab, crypt_tab=crypt_tab)

if device is None:
if vfstype == "swap":
Expand All @@ -557,7 +540,7 @@ def get_device(self, devicetree, spec=None, file=None, vfstype=None,
parents=devicetree.resolve_device(_spec),
fmt=get_format(vfstype, device=_spec, exists=True),
exists=True)
elif vfstype == "bind" or (_mntops is not None and "bind" in _mntops):
elif vfstype == "bind" or (_mntopts is not None and "bind" in _mntopts):
# bind mount... set vfstype so later comparison won't
# turn up false positives
vfstype = "bind"
Expand All @@ -576,23 +559,23 @@ def get_device(self, devicetree, spec=None, file=None, vfstype=None,
if hasattr(device.format, "mountpoint"):
device.format.mountpoint = file

device.format.options = _mntops
device.format.options = _mntopts

return device

def add_entry(self, spec=None, file=None, vfstype=None, mntops=None,
def add_entry(self, spec=None, file=None, vfstype=None, mntopts=None,
freq=None, passno=None, comment=None, *, entry=None):
""" Add a new entry into the table
If both entry and other values are given, these values are prioritized over entry values.
If mntops/freq/passno is not set uses their respective default values.
If mntopts/freq/passno is not set uses their respective default values.

:keyword spec: device specs (see man fstab(5) fs_spec)
:type spec: str
:keyword file: device mount path (see man fstab(5) fs_file)
:type file: str
:keyword vfstype: device file system type (see man fstab(5) fs_vfstype)
:type vfstype: str
:keyword mntops: list of mount option strings (see man fstab(5) fs_mntops)
:keyword mntopts: list of mount option strings (see man fstab(5) fs_mntopts)
:type mnops: list
:keyword freq: whether to dump the filesystem (see man fstab(5) fs_freq)
:type freq: int
Expand All @@ -615,10 +598,10 @@ def add_entry(self, spec=None, file=None, vfstype=None, mntops=None,
if vfstype is not None:
_entry.vfstype = vfstype

if mntops is not None:
_entry.mntops = mntops
if _entry.mntops is None:
_entry.mntops = ['defaults']
if mntopts is not None:
_entry.mntopts = mntopts
if _entry.mntopts is None:
_entry.mntopts = ['defaults']

if freq is not None:
_entry.freq = freq
Expand Down Expand Up @@ -762,8 +745,8 @@ def _get_spec(self, device):

spec = None

if hasattr(device.format, 'fstab') and device.format.fstab.spec_type:
spec_type = device.format.fstab.spec_type
if hasattr(device.format, "fstab_spec_type") and device.format.fstab_spec_type:
spec_type = device.format.fstab_spec_type
else:
spec_type = self.spec_type

Expand Down Expand Up @@ -825,25 +808,25 @@ def update(self, action, bae_entry):
# device is not present in fstab and has a defined mountpoint => add it
self.add_entry(spec=spec,
file=action.device.format.mountpoint,
mntops=action.device.format.fstab.mntops,
freq=action.device.format.fstab.freq,
passno=action.device.format.fstab.passno,
mntopts=action.device.format.options,
freq=action.device.format.freq,
passno=action.device.format.passno,
entry=entry)
elif found and found.spec != spec and action.device.format.mountpoint is not None:
# allow change of spec of existing devices
self.remove_entry(entry=found)
self.add_entry(spec=spec,
mntops=action.device.format.fstab.mntops,
freq=action.device.format.fstab.freq,
passno=action.device.format.fstab.passno,
mntopts=action.device.format.options,
freq=action.device.format.freq,
passno=action.device.format.passno,
entry=found)
elif found and found.file != action.device.format.mountpoint and action.device.format.mountpoint is not None:
# device already exists in fstab but with a different mountpoint => add it
self.add_entry(spec=spec,
file=action.device.format.mountpoint,
mntops=action.device.format.fstab.mntops,
freq=action.device.format.fstab.freq,
passno=action.device.format.fstab.passno,
mntopts=action.device.format.options,
freq=action.device.format.freq,
passno=action.device.format.passno,
entry=found)
return

Expand All @@ -858,9 +841,9 @@ def update(self, action, bae_entry):
self.remove_entry(entry=bae_entry)
self.add_entry(spec=spec,
file=action.device.format.mountpoint,
mntops=action.device.format.fstab.mntops,
freq=action.device.format.fstab.freq,
passno=action.device.format.fstab.passno,
mntopts=action.device.format.options,
freq=action.device.format.freq,
passno=action.device.format.passno,
entry=bae_entry)
elif action.device.format.mountpoint is None:
self.remove_entry(entry=bae_entry)
Loading