Skip to content

Commit

Permalink
Added support for user defined values in fstab
Browse files Browse the repository at this point in the history
- Allows to set prefered spec type in fstab by either setting
blivet.fstab.spec_type for 'global' effect or device.format.fstab.spec_type for individual device.
Allowed values are "UUID", "PARTUUID", "LABEL", "PARTLABEL" or "PATH".
When selected value is not available, it uses other one (defaults to
UUID).

- Allows to set freq, passno and mntops for individual devices to be
  written in fstab. Done by setting device.format.fstab.freq/passno/mntops values respectively.
  • Loading branch information
japokorn committed Jun 26, 2023
1 parent 7f5ef65 commit 20652d2
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 13 deletions.
3 changes: 3 additions & 0 deletions blivet/formats/fs.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
from . import DeviceFormat, register_device_format
from .. import util
from ..flags import flags
from ..fstab import FSTabOptions
from ..storage_log import log_exception_info, log_method_call
from .. import arch
from ..size import Size, ROUND_UP
Expand Down Expand Up @@ -121,6 +122,8 @@ 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 Down
87 changes: 83 additions & 4 deletions blivet/fstab.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,22 @@
import logging
log = logging.getLogger("blivet")

class FSTabOptions(object):
""" User prefered 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

# prefered 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
Expand Down Expand Up @@ -277,6 +293,10 @@ def __init__(self, src_file=None, dest_file=None):
self.src_file = src_file
self.dest_file = dest_file

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

if self.src_file is not None:
self.read(self.src_file)

Expand Down Expand Up @@ -535,7 +555,7 @@ def add_entry(self, spec=None, file=None, vfstype=None, mntops=None,
_entry.freq = 0

if passno is not None:
_entry.passno = freq
_entry.passno = passno
elif _entry.passno is None:
# 'passno' represents order of fsck run at the boot time (default: 0, i.e. disabled).
# '/' should have 1, other checked should have 2
Expand Down Expand Up @@ -664,6 +684,34 @@ def find_entry(self, spec=None, file=None, *, entry=None):

return None

def get_spec(self, device):
""" Resolve which device spec should be used and return it in a form accepted by fstab.
Returns None if desired spec was not found
"""

# Use device specific spec type if it is set
# Use "globally" set (on FSTabManager level) spec type otherwise

spec = None
spec_type = device.format.fstab.spec_type or self.spec_type

if spec_type == "LABEL" and device.format.label:
spec = "LABEL=%s" % device.format.label
elif spec_type == "PARTUUID" and device.uuid:
spec = "PARTUUID=%s" % device.uuid
elif spec_type == "PARTLABEL" and device.format.name:
spec = "PARTLABEL=%s" % device.format.name
elif spec_type == "PATH":
spec = device.path
elif device.format.uuid:
# default choice
spec = "UUID=%s" % device.format.uuid
else:
# if everything else failed, let blivet decide
return None

return spec

def update(self, action, bae_entry):
""" Update fstab based on action type and device. Does not commit changes to a file.
Expand Down Expand Up @@ -691,20 +739,51 @@ def update(self, action, bae_entry):
# make sure it is not already present there
entry = self.entry_from_device(action.device)
found = self.find_entry(entry=entry)

# get correct spec type to use (if None, the one already present in entry is used)
spec = self.get_spec(action.device)

if found is None and action.device.format.mountpoint is not None:
# device is not present in fstab and has a defined mountpoint => add it
self.add_entry(entry=entry)
self.add_entry(spec=spec,
mntops=action.device.format.fstab.mntops,
freq=action.device.format.fstab.freq,
passno=action.device.format.fstab.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,
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(file=action.device.format.mountpoint, entry=found)
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,
entry=found)
return

if action.is_configure and action.is_format and bae_entry is not None:
# Handle change of the mountpoint:
# Change its value if it is defined, remove the fstab entry if it is None

entry = self.entry_from_device(action.device)

# get correct spec type to use (if None, the one already present in entry is used)
spec = self.get_spec(action.device)

if action.device.format.mountpoint is not None and bae_entry.file != action.device.format.mountpoint:
self.remove_entry(entry=bae_entry)
self.add_entry(file=action.device.format.mountpoint, 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,
entry=bae_entry)
elif action.device.format.mountpoint is None:
self.remove_entry(entry=bae_entry)
18 changes: 9 additions & 9 deletions tests/storage_tests/fstab_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ def test_fstab(self):
parents=[vg], name="blivetTestLVMine")
self.storage.create_device(lv)

# specify device spec representation in fstab
lv.format.fstab.spec_type="PATH"
lv.format.fstab.freq=54321
lv.format.fstab.passno=12345
lv.format.fstab.mntops=['optionA', 'optionB']

# Change the mountpoint, make sure the change will make it into the fstab
ac = blivet.deviceaction.ActionConfigureFormat(device=lv, attr="mountpoint", new_value="/mnt/test2")
self.storage.devicetree.actions.add(ac)
Expand All @@ -75,7 +81,9 @@ def test_fstab(self):
with open(fstab_path, "r") as f:
contents = f.read()
self.assertTrue("blivetTestLVMine" in contents)
self.assertTrue("/mnt/test2" in contents)
self.assertTrue("54321" in contents)
self.assertTrue("12345" in contents)
self.assertTrue("optionA,optionB" in contents)

dev = self.storage.devicetree.get_device_by_name("blivetTestVG-blivetTestLVMine")
self.storage.recursive_remove(dev)
Expand All @@ -89,12 +97,4 @@ def test_fstab(self):
self.assertFalse("blivetTestLVMine" in contents)
self.assertFalse("/mnt/test2" in contents)

def test_get_device(self):
disk = self.storage.devicetree.get_device_by_path(self.vdevs[0])
self.assertIsNotNone(disk)

with tempfile.TemporaryDirectory() as tmpdirname:
fstab_path = os.path.join(tmpdirname, 'fstab')

# change write path of blivet.fstab
self.storage.fstab.dest_file = fstab_path

0 comments on commit 20652d2

Please sign in to comment.