mirror of
https://github.com/CHN-beta/nixpkgs.git
synced 2026-01-13 11:30:35 +08:00
This version largely differs from the previous version in that we now set the UUID via the "uuid" keyword argument rather than introducing a new "new_uuid" kwarg. We now need to reorder the uuids.patch and the ntfs-formattable.patch, because the latter got merged into the upstream 2.1-devel branch and the uuids.patch has been rebased against the newest HEAD of 2.1-devel. Signed-off-by: aszlig <aszlig@redmoonstudios.org>
973 lines
32 KiB
Diff
973 lines
32 KiB
Diff
Submitted upstream at:
|
|
|
|
https://github.com/rhinstaller/blivet/pull/537
|
|
|
|
diff --git a/blivet/errors.py b/blivet/errors.py
|
|
index a39a5fbe..8d4c6463 100644
|
|
--- a/blivet/errors.py
|
|
+++ b/blivet/errors.py
|
|
@@ -109,6 +109,10 @@ class FSWriteLabelError(FSError):
|
|
pass
|
|
|
|
|
|
+class FSWriteUUIDError(FSError):
|
|
+ pass
|
|
+
|
|
+
|
|
class FSReadLabelError(FSError):
|
|
pass
|
|
|
|
diff --git a/blivet/formats/fs.py b/blivet/formats/fs.py
|
|
index 30e65536..631706ed 100644
|
|
--- a/blivet/formats/fs.py
|
|
+++ b/blivet/formats/fs.py
|
|
@@ -38,9 +38,11 @@ from ..tasks import fsreadlabel
|
|
from ..tasks import fsresize
|
|
from ..tasks import fssize
|
|
from ..tasks import fssync
|
|
+from ..tasks import fsuuid
|
|
from ..tasks import fswritelabel
|
|
+from ..tasks import fswriteuuid
|
|
from ..errors import FormatCreateError, FSError, FSReadLabelError
|
|
-from ..errors import FSWriteLabelError
|
|
+from ..errors import FSWriteLabelError, FSWriteUUIDError
|
|
from . import DeviceFormat, register_device_format
|
|
from .. import util
|
|
from .. import platform
|
|
@@ -65,12 +67,14 @@ class FS(DeviceFormat):
|
|
_name = None
|
|
_modules = [] # kernel modules required for support
|
|
_labelfs = None # labeling functionality
|
|
+ _uuidfs = None # functionality for UUIDs
|
|
_fsck_class = fsck.UnimplementedFSCK
|
|
_mkfs_class = fsmkfs.UnimplementedFSMkfs
|
|
_mount_class = fsmount.FSMount
|
|
_readlabel_class = fsreadlabel.UnimplementedFSReadLabel
|
|
_sync_class = fssync.UnimplementedFSSync
|
|
_writelabel_class = fswritelabel.UnimplementedFSWriteLabel
|
|
+ _writeuuid_class = fswriteuuid.UnimplementedFSWriteUUID
|
|
# This constant is aquired by testing some filesystems
|
|
# and it's giving us percentage of space left after the format.
|
|
# This number is more guess than precise number because this
|
|
@@ -111,6 +115,7 @@ class FS(DeviceFormat):
|
|
self._readlabel = self._readlabel_class(self)
|
|
self._sync = self._sync_class(self)
|
|
self._writelabel = self._writelabel_class(self)
|
|
+ self._writeuuid = self._writeuuid_class(self)
|
|
|
|
self._current_info = None # info obtained by _info task
|
|
|
|
@@ -249,6 +254,31 @@ class FS(DeviceFormat):
|
|
label = property(lambda s: s._get_label(), lambda s, l: s._set_label(l),
|
|
doc="this filesystem's label")
|
|
|
|
+ def can_assign_uuid(self):
|
|
+ """Returns True if this filesystem supports setting an UUID during
|
|
+ creation, otherwise False.
|
|
+
|
|
+ :rtype: bool
|
|
+ """
|
|
+ return self._mkfs.can_set_uuid and self._mkfs.available
|
|
+
|
|
+ def can_reassign_uuid(self):
|
|
+ """Returns True if it's possible to set the UUID of this filesystem
|
|
+ after it has been created, otherwise False.
|
|
+
|
|
+ :rtype: bool
|
|
+ """
|
|
+ return self._writeuuid.available
|
|
+
|
|
+ def uuid_format_ok(self, uuid):
|
|
+ """Return True if the UUID has an acceptable format for this
|
|
+ filesystem.
|
|
+
|
|
+ :param uuid: An UUID
|
|
+ :type uuid: str
|
|
+ """
|
|
+ return self._uuidfs is not None and self._uuidfs.uuid_format_ok(uuid)
|
|
+
|
|
def update_size_info(self):
|
|
""" Update this filesystem's current and minimum size (for resize). """
|
|
|
|
@@ -358,9 +388,16 @@ class FS(DeviceFormat):
|
|
|
|
super(FS, self)._create()
|
|
try:
|
|
- self._mkfs.do_task(options=kwargs.get("options"), label=not self.relabels())
|
|
+ self._mkfs.do_task(options=kwargs.get("options"),
|
|
+ label=not self.relabels(),
|
|
+ set_uuid=self.can_assign_uuid())
|
|
except FSWriteLabelError as e:
|
|
log.warning("Choosing not to apply label (%s) during creation of filesystem %s. Label format is unacceptable for this filesystem.", self.label, self.type)
|
|
+ except FSWriteUUIDError as e:
|
|
+ log.warning("Choosing not to apply UUID (%s) during"
|
|
+ " creation of filesystem %s. UUID format"
|
|
+ " is unacceptable for this filesystem.",
|
|
+ self.uuid, self.type)
|
|
except FSError as e:
|
|
raise FormatCreateError(e, self.device)
|
|
|
|
@@ -371,6 +408,9 @@ class FS(DeviceFormat):
|
|
self.write_label()
|
|
except FSError as e:
|
|
log.warning("Failed to write label (%s) for filesystem %s: %s", self.label, self.type, e)
|
|
+ if self.uuid is not None and not self.can_assign_uuid() and \
|
|
+ self.can_reassign_uuid():
|
|
+ self.write_uuid()
|
|
|
|
def _post_resize(self):
|
|
self.do_check()
|
|
@@ -607,6 +647,35 @@ class FS(DeviceFormat):
|
|
|
|
self._writelabel.do_task()
|
|
|
|
+ def write_uuid(self):
|
|
+ """Set an UUID for this filesystem.
|
|
+
|
|
+ :raises: FSError
|
|
+
|
|
+ Raises an FSError if the UUID can not be set.
|
|
+ """
|
|
+ err = None
|
|
+
|
|
+ if self.uuid is None:
|
|
+ err = "makes no sense to write an UUID when not requested"
|
|
+
|
|
+ if not self.exists:
|
|
+ err = "filesystem has not been created"
|
|
+
|
|
+ if not self._writeuuid.available:
|
|
+ err = "no application to set UUID for filesystem %s" % self.type
|
|
+
|
|
+ if not self.uuid_format_ok(self.uuid):
|
|
+ err = "bad UUID format for application %s" % self._writeuuid
|
|
+
|
|
+ if not os.path.exists(self.device):
|
|
+ err = "device does not exist"
|
|
+
|
|
+ if err is not None:
|
|
+ raise FSError(err)
|
|
+
|
|
+ self._writeuuid.do_task()
|
|
+
|
|
@property
|
|
def utils_available(self):
|
|
# we aren't checking for fsck because we shouldn't need it
|
|
@@ -720,6 +789,7 @@ class Ext2FS(FS):
|
|
_type = "ext2"
|
|
_modules = ["ext2"]
|
|
_labelfs = fslabeling.Ext2FSLabeling()
|
|
+ _uuidfs = fsuuid.Ext2FSUUID()
|
|
_packages = ["e2fsprogs"]
|
|
_formattable = True
|
|
_supported = True
|
|
@@ -736,6 +806,7 @@ class Ext2FS(FS):
|
|
_resize_class = fsresize.Ext2FSResize
|
|
_size_info_class = fssize.Ext2FSSize
|
|
_writelabel_class = fswritelabel.Ext2FSWriteLabel
|
|
+ _writeuuid_class = fswriteuuid.Ext2FSWriteUUID
|
|
parted_system = fileSystemType["ext2"]
|
|
_metadata_size_factor = 0.93 # ext2 metadata may take 7% of space
|
|
|
|
@@ -779,6 +850,7 @@ class FATFS(FS):
|
|
_type = "vfat"
|
|
_modules = ["vfat"]
|
|
_labelfs = fslabeling.FATFSLabeling()
|
|
+ _uuidfs = fsuuid.FATFSUUID()
|
|
_supported = True
|
|
_formattable = True
|
|
_max_size = Size("1 TiB")
|
|
@@ -788,6 +860,7 @@ class FATFS(FS):
|
|
_mount_class = fsmount.FATFSMount
|
|
_readlabel_class = fsreadlabel.DosFSReadLabel
|
|
_writelabel_class = fswritelabel.DosFSWriteLabel
|
|
+ _writeuuid_class = fswriteuuid.DosFSWriteUUID
|
|
_metadata_size_factor = 0.99 # fat metadata may take 1% of space
|
|
# FIXME this should be fat32 in some cases
|
|
parted_system = fileSystemType["fat16"]
|
|
@@ -887,6 +960,7 @@ class JFS(FS):
|
|
_type = "jfs"
|
|
_modules = ["jfs"]
|
|
_labelfs = fslabeling.JFSLabeling()
|
|
+ _uuidfs = fsuuid.JFSUUID()
|
|
_max_size = Size("8 TiB")
|
|
_formattable = True
|
|
_linux_native = True
|
|
@@ -896,6 +970,7 @@ class JFS(FS):
|
|
_mkfs_class = fsmkfs.JFSMkfs
|
|
_size_info_class = fssize.JFSSize
|
|
_writelabel_class = fswritelabel.JFSWriteLabel
|
|
+ _writeuuid_class = fswriteuuid.JFSWriteUUID
|
|
_metadata_size_factor = 0.99 # jfs metadata may take 1% of space
|
|
parted_system = fileSystemType["jfs"]
|
|
|
|
@@ -912,6 +987,7 @@ class ReiserFS(FS):
|
|
""" reiserfs filesystem """
|
|
_type = "reiserfs"
|
|
_labelfs = fslabeling.ReiserFSLabeling()
|
|
+ _uuidfs = fsuuid.ReiserFSUUID()
|
|
_modules = ["reiserfs"]
|
|
_max_size = Size("16 TiB")
|
|
_formattable = True
|
|
@@ -923,6 +999,7 @@ class ReiserFS(FS):
|
|
_mkfs_class = fsmkfs.ReiserFSMkfs
|
|
_size_info_class = fssize.ReiserFSSize
|
|
_writelabel_class = fswritelabel.ReiserFSWriteLabel
|
|
+ _writeuuid_class = fswriteuuid.ReiserFSWriteUUID
|
|
_metadata_size_factor = 0.98 # reiserfs metadata may take 2% of space
|
|
parted_system = fileSystemType["reiserfs"]
|
|
|
|
@@ -940,6 +1017,7 @@ class XFS(FS):
|
|
_type = "xfs"
|
|
_modules = ["xfs"]
|
|
_labelfs = fslabeling.XFSLabeling()
|
|
+ _uuidfs = fsuuid.XFSUUID()
|
|
_max_size = Size("16 EiB")
|
|
_formattable = True
|
|
_linux_native = True
|
|
@@ -951,6 +1029,7 @@ class XFS(FS):
|
|
_size_info_class = fssize.XFSSize
|
|
_sync_class = fssync.XFSSync
|
|
_writelabel_class = fswritelabel.XFSWriteLabel
|
|
+ _writeuuid_class = fswriteuuid.XFSWriteUUID
|
|
_metadata_size_factor = 0.97 # xfs metadata may take 3% of space
|
|
parted_system = fileSystemType["xfs"]
|
|
|
|
@@ -990,6 +1069,7 @@ class HFSPlus(FS):
|
|
_udev_types = ["hfsplus"]
|
|
_packages = ["hfsplus-tools"]
|
|
_labelfs = fslabeling.HFSPlusLabeling()
|
|
+ _uuidfs = fsuuid.HFSPlusUUID()
|
|
_formattable = True
|
|
_min_size = Size("1 MiB")
|
|
_max_size = Size("2 TiB")
|
|
@@ -1026,6 +1106,7 @@ class NTFS(FS):
|
|
""" ntfs filesystem. """
|
|
_type = "ntfs"
|
|
_labelfs = fslabeling.NTFSLabeling()
|
|
+ _uuidfs = fsuuid.NTFSUUID()
|
|
_resizable = True
|
|
_formattable = True
|
|
_min_size = Size("1 MiB")
|
|
@@ -1040,6 +1121,7 @@ class NTFS(FS):
|
|
_resize_class = fsresize.NTFSResize
|
|
_size_info_class = fssize.NTFSSize
|
|
_writelabel_class = fswritelabel.NTFSWriteLabel
|
|
+ _writeuuid_class = fswriteuuid.NTFSWriteUUID
|
|
parted_system = fileSystemType["ntfs"]
|
|
|
|
register_device_format(NTFS)
|
|
diff --git a/blivet/formats/swap.py b/blivet/formats/swap.py
|
|
index 0818f586..66a311a9 100644
|
|
--- a/blivet/formats/swap.py
|
|
+++ b/blivet/formats/swap.py
|
|
@@ -21,8 +21,10 @@
|
|
#
|
|
|
|
from parted import PARTITION_SWAP, fileSystemType
|
|
+from ..errors import FSWriteUUIDError
|
|
from ..storage_log import log_method_call
|
|
from ..tasks import availability
|
|
+from ..tasks import fsuuid
|
|
from . import DeviceFormat, register_device_format
|
|
from ..size import Size
|
|
|
|
@@ -110,6 +112,10 @@ class SwapSpace(DeviceFormat):
|
|
label = property(lambda s: s._get_label(), lambda s, l: s._set_label(l),
|
|
doc="the label for this swap space")
|
|
|
|
+ def uuid_format_ok(self, uuid):
|
|
+ """Check whether the given UUID is correct according to RFC 4122."""
|
|
+ return fsuuid.FSUUID._check_rfc4122_uuid(uuid)
|
|
+
|
|
def _set_priority(self, priority):
|
|
# pylint: disable=attribute-defined-outside-init
|
|
if priority is None:
|
|
@@ -167,6 +173,12 @@ class SwapSpace(DeviceFormat):
|
|
def _create(self, **kwargs):
|
|
log_method_call(self, device=self.device,
|
|
type=self.type, status=self.status)
|
|
- blockdev.swap.mkswap(self.device, label=self.label)
|
|
+ if self.uuid is None:
|
|
+ blockdev.swap.mkswap(self.device, label=self.label)
|
|
+ else:
|
|
+ if not self.uuid_format_ok(self.uuid):
|
|
+ raise FSWriteUUIDError("bad UUID format for swap filesystem")
|
|
+ blockdev.swap.mkswap(self.device, label=self.label,
|
|
+ extra={"-U": self.uuid})
|
|
|
|
register_device_format(SwapSpace)
|
|
diff --git a/blivet/tasks/availability.py b/blivet/tasks/availability.py
|
|
index 7811be17..4a134d79 100644
|
|
--- a/blivet/tasks/availability.py
|
|
+++ b/blivet/tasks/availability.py
|
|
@@ -298,12 +298,14 @@ MKFS_JFS_APP = application("mkfs.jfs")
|
|
MKFS_XFS_APP = application("mkfs.xfs")
|
|
MKNTFS_APP = application("mkntfs")
|
|
MKREISERFS_APP = application("mkreiserfs")
|
|
+MLABEL_APP = application("mlabel")
|
|
MULTIPATH_APP = application("multipath")
|
|
NTFSINFO_APP = application("ntfsinfo")
|
|
NTFSLABEL_APP = application("ntfslabel")
|
|
NTFSRESIZE_APP = application("ntfsresize")
|
|
REISERFSTUNE_APP = application("reiserfstune")
|
|
RESIZE2FS_APP = application_by_package("resize2fs", E2FSPROGS_PACKAGE)
|
|
+TUNE2FS_APP = application_by_package("tune2fs", E2FSPROGS_PACKAGE)
|
|
XFSADMIN_APP = application("xfs_admin")
|
|
XFSDB_APP = application("xfs_db")
|
|
XFSFREEZE_APP = application("xfs_freeze")
|
|
diff --git a/blivet/tasks/fsmkfs.py b/blivet/tasks/fsmkfs.py
|
|
index d0da5b21..ad166aa0 100644
|
|
--- a/blivet/tasks/fsmkfs.py
|
|
+++ b/blivet/tasks/fsmkfs.py
|
|
@@ -24,7 +24,7 @@ import shlex
|
|
|
|
from six import add_metaclass
|
|
|
|
-from ..errors import FSError, FSWriteLabelError
|
|
+from ..errors import FSError, FSWriteLabelError, FSWriteUUIDError
|
|
from .. import util
|
|
|
|
from . import availability
|
|
@@ -36,6 +36,7 @@ from . import task
|
|
class FSMkfsTask(fstask.FSTask):
|
|
|
|
can_label = abc.abstractproperty(doc="whether this task labels")
|
|
+ can_set_uuid = abc.abstractproperty(doc="whether this task can set UUID")
|
|
|
|
|
|
@add_metaclass(abc.ABCMeta)
|
|
@@ -49,6 +50,16 @@ class FSMkfs(task.BasicApplication, FSMkfsTask):
|
|
|
|
args = abc.abstractproperty(doc="options for creating filesystem")
|
|
|
|
+ @abc.abstractmethod
|
|
+ def get_uuid_args(self, uuid):
|
|
+ """Return a list of arguments for setting a filesystem UUID.
|
|
+
|
|
+ :param uuid: the UUID to set
|
|
+ :type uuid: str
|
|
+ :rtype: list of str
|
|
+ """
|
|
+ raise NotImplementedError
|
|
+
|
|
# IMPLEMENTATION methods
|
|
|
|
@property
|
|
@@ -61,6 +72,15 @@ class FSMkfs(task.BasicApplication, FSMkfsTask):
|
|
return self.label_option is not None
|
|
|
|
@property
|
|
+ def can_set_uuid(self):
|
|
+ """Whether this task can set the UUID of a filesystem.
|
|
+
|
|
+ :returns: True if UUID can be set
|
|
+ :rtype: bool
|
|
+ """
|
|
+ return self.get_uuid_args is not None
|
|
+
|
|
+ @property
|
|
def _label_options(self):
|
|
""" Any labeling options that a particular filesystem may use.
|
|
|
|
@@ -80,13 +100,33 @@ class FSMkfs(task.BasicApplication, FSMkfsTask):
|
|
else:
|
|
raise FSWriteLabelError("Choosing not to apply label (%s) during creation of filesystem %s. Label format is unacceptable for this filesystem." % (self.fs.label, self.fs.type))
|
|
|
|
- def _format_options(self, options=None, label=False):
|
|
+ @property
|
|
+ def _uuid_options(self):
|
|
+ """Any UUID options that a particular filesystem may use.
|
|
+
|
|
+ :returns: UUID options
|
|
+ :rtype: list of str
|
|
+ :raises: FSWriteUUIDError
|
|
+ """
|
|
+ if self.get_uuid_args is None or self.fs.uuid is None:
|
|
+ return []
|
|
+
|
|
+ if self.fs.uuid_format_ok(self.fs.uuid):
|
|
+ return self.get_uuid_args(self.fs.uuid)
|
|
+ else:
|
|
+ raise FSWriteUUIDError("Choosing not to apply UUID (%s) during"
|
|
+ " creation of filesystem %s. UUID format"
|
|
+ " is unacceptable for this filesystem."
|
|
+ % (self.fs.uuid, self.fs.type))
|
|
+
|
|
+ def _format_options(self, options=None, label=False, set_uuid=False):
|
|
"""Get a list of format options to be used when creating the
|
|
filesystem.
|
|
|
|
:param options: any special options
|
|
:type options: list of str or None
|
|
:param bool label: if True, label if possible, default is False
|
|
+ :param bool set_uuid: whether set UUID if possible, default is False
|
|
"""
|
|
options = options or []
|
|
|
|
@@ -94,25 +134,33 @@ class FSMkfs(task.BasicApplication, FSMkfsTask):
|
|
raise FSError("options parameter must be a list.")
|
|
|
|
label_options = self._label_options if label else []
|
|
+ uuid_options = self._uuid_options if set_uuid else []
|
|
create_options = shlex.split(self.fs.create_options or "")
|
|
- return options + self.args + label_options + create_options + [self.fs.device]
|
|
+ return (options + self.args + label_options + uuid_options +
|
|
+ create_options + [self.fs.device])
|
|
|
|
- def _mkfs_command(self, options, label):
|
|
+ def _mkfs_command(self, options, label, set_uuid):
|
|
"""Return the command to make the filesystem.
|
|
|
|
:param options: any special options
|
|
:type options: list of str or None
|
|
+ :param label: whether to set a label
|
|
+ :type label: bool
|
|
+ :param set_uuid: whether to set an UUID
|
|
+ :type set_uuid: bool
|
|
:returns: the mkfs command
|
|
:rtype: list of str
|
|
"""
|
|
- return [str(self.ext)] + self._format_options(options, label)
|
|
+ return [str(self.ext)] + self._format_options(options, label, set_uuid)
|
|
|
|
- def do_task(self, options=None, label=False):
|
|
+ def do_task(self, options=None, label=False, set_uuid=False):
|
|
"""Create the format on the device and label if possible and desired.
|
|
|
|
:param options: any special options, may be None
|
|
:type options: list of str or NoneType
|
|
:param bool label: whether to label while creating, default is False
|
|
+ :param bool set_uuid: whether to set an UUID while creating, default
|
|
+ is False
|
|
"""
|
|
# pylint: disable=arguments-differ
|
|
error_msgs = self.availability_errors
|
|
@@ -120,8 +168,9 @@ class FSMkfs(task.BasicApplication, FSMkfsTask):
|
|
raise FSError("\n".join(error_msgs))
|
|
|
|
options = options or []
|
|
+ cmd = self._mkfs_command(options, label, set_uuid)
|
|
try:
|
|
- ret = util.run_program(self._mkfs_command(options, label))
|
|
+ ret = util.run_program(cmd)
|
|
except OSError as e:
|
|
raise FSError(e)
|
|
|
|
@@ -133,6 +182,9 @@ class BTRFSMkfs(FSMkfs):
|
|
ext = availability.MKFS_BTRFS_APP
|
|
label_option = None
|
|
|
|
+ def get_uuid_args(self, uuid):
|
|
+ return ["-U", uuid]
|
|
+
|
|
@property
|
|
def args(self):
|
|
return []
|
|
@@ -144,6 +196,9 @@ class Ext2FSMkfs(FSMkfs):
|
|
|
|
_opts = []
|
|
|
|
+ def get_uuid_args(self, uuid):
|
|
+ return ["-U", uuid]
|
|
+
|
|
@property
|
|
def args(self):
|
|
return self._opts + (["-T", self.fs.fsprofile] if self.fs.fsprofile else [])
|
|
@@ -161,6 +216,9 @@ class FATFSMkfs(FSMkfs):
|
|
ext = availability.MKDOSFS_APP
|
|
label_option = "-n"
|
|
|
|
+ def get_uuid_args(self, uuid):
|
|
+ return ["-i", uuid.replace('-', '')]
|
|
+
|
|
@property
|
|
def args(self):
|
|
return []
|
|
@@ -169,6 +227,7 @@ class FATFSMkfs(FSMkfs):
|
|
class GFS2Mkfs(FSMkfs):
|
|
ext = availability.MKFS_GFS2_APP
|
|
label_option = None
|
|
+ get_uuid_args = None
|
|
|
|
@property
|
|
def args(self):
|
|
@@ -178,6 +237,7 @@ class GFS2Mkfs(FSMkfs):
|
|
class HFSMkfs(FSMkfs):
|
|
ext = availability.HFORMAT_APP
|
|
label_option = "-l"
|
|
+ get_uuid_args = None
|
|
|
|
@property
|
|
def args(self):
|
|
@@ -187,6 +247,7 @@ class HFSMkfs(FSMkfs):
|
|
class HFSPlusMkfs(FSMkfs):
|
|
ext = availability.MKFS_HFSPLUS_APP
|
|
label_option = "-v"
|
|
+ get_uuid_args = None
|
|
|
|
@property
|
|
def args(self):
|
|
@@ -196,6 +257,7 @@ class HFSPlusMkfs(FSMkfs):
|
|
class JFSMkfs(FSMkfs):
|
|
ext = availability.MKFS_JFS_APP
|
|
label_option = "-L"
|
|
+ get_uuid_args = None
|
|
|
|
@property
|
|
def args(self):
|
|
@@ -205,6 +267,7 @@ class JFSMkfs(FSMkfs):
|
|
class NTFSMkfs(FSMkfs):
|
|
ext = availability.MKNTFS_APP
|
|
label_option = "-L"
|
|
+ get_uuid_args = None
|
|
|
|
@property
|
|
def args(self):
|
|
@@ -215,6 +278,9 @@ class ReiserFSMkfs(FSMkfs):
|
|
ext = availability.MKREISERFS_APP
|
|
label_option = "-l"
|
|
|
|
+ def get_uuid_args(self, uuid):
|
|
+ return ["-u", uuid]
|
|
+
|
|
@property
|
|
def args(self):
|
|
return ["-f", "-f"]
|
|
@@ -224,6 +290,9 @@ class XFSMkfs(FSMkfs):
|
|
ext = availability.MKFS_XFS_APP
|
|
label_option = "-L"
|
|
|
|
+ def get_uuid_args(self, uuid):
|
|
+ return ["-m", "uuid=" + uuid]
|
|
+
|
|
@property
|
|
def args(self):
|
|
return ["-f"]
|
|
@@ -234,3 +303,7 @@ class UnimplementedFSMkfs(task.UnimplementedTask, FSMkfsTask):
|
|
@property
|
|
def can_label(self):
|
|
return False
|
|
+
|
|
+ @property
|
|
+ def can_set_uuid(self):
|
|
+ return False
|
|
diff --git a/blivet/tasks/fsuuid.py b/blivet/tasks/fsuuid.py
|
|
new file mode 100644
|
|
index 00000000..5beb43ac
|
|
--- /dev/null
|
|
+++ b/blivet/tasks/fsuuid.py
|
|
@@ -0,0 +1,85 @@
|
|
+import abc
|
|
+
|
|
+from six import add_metaclass
|
|
+
|
|
+
|
|
+@add_metaclass(abc.ABCMeta)
|
|
+class FSUUID(object):
|
|
+
|
|
+ """An abstract class that represents filesystem actions for setting the
|
|
+ UUID.
|
|
+ """
|
|
+
|
|
+ @abc.abstractmethod
|
|
+ def uuid_format_ok(self, uuid):
|
|
+ """Returns True if the given UUID is correctly formatted for
|
|
+ this filesystem, otherwise False.
|
|
+
|
|
+ :param str uuid: the UUID for this filesystem
|
|
+ :rtype: bool
|
|
+ """
|
|
+ raise NotImplementedError
|
|
+
|
|
+ # IMPLEMENTATION methods
|
|
+
|
|
+ @classmethod
|
|
+ def _check_rfc4122_uuid(cls, uuid):
|
|
+ """Check whether the given UUID is correct according to RFC 4122 and
|
|
+ return True if it's correct or False otherwise.
|
|
+
|
|
+ :param str uuid: the UUID to check
|
|
+ :rtype: bool
|
|
+ """
|
|
+ chunks = uuid.split('-')
|
|
+ if len(chunks) != 5:
|
|
+ return False
|
|
+ chunklens = [len(chunk) for chunk in chunks
|
|
+ if all(char in "0123456789abcdef" for char in chunk)]
|
|
+ return chunklens == [8, 4, 4, 4, 12]
|
|
+
|
|
+
|
|
+class Ext2FSUUID(FSUUID):
|
|
+ @classmethod
|
|
+ def uuid_format_ok(cls, uuid):
|
|
+ return cls._check_rfc4122_uuid(uuid)
|
|
+
|
|
+
|
|
+class FATFSUUID(FSUUID):
|
|
+ @classmethod
|
|
+ def uuid_format_ok(cls, uuid):
|
|
+ if len(uuid) != 9 or uuid[4] != '-':
|
|
+ return False
|
|
+ return all(char in "0123456789ABCDEF"
|
|
+ for char in (uuid[:4] + uuid[5:]))
|
|
+
|
|
+
|
|
+class JFSUUID(FSUUID):
|
|
+ @classmethod
|
|
+ def uuid_format_ok(cls, uuid):
|
|
+ return cls._check_rfc4122_uuid(uuid)
|
|
+
|
|
+
|
|
+class ReiserFSUUID(FSUUID):
|
|
+ @classmethod
|
|
+ def uuid_format_ok(cls, uuid):
|
|
+ return cls._check_rfc4122_uuid(uuid)
|
|
+
|
|
+
|
|
+class XFSUUID(FSUUID):
|
|
+ @classmethod
|
|
+ def uuid_format_ok(cls, uuid):
|
|
+ return cls._check_rfc4122_uuid(uuid)
|
|
+
|
|
+
|
|
+class HFSPlusUUID(FSUUID):
|
|
+ @classmethod
|
|
+ def uuid_format_ok(cls, uuid):
|
|
+ return cls._check_rfc4122_uuid(uuid)
|
|
+
|
|
+
|
|
+class NTFSUUID(FSUUID):
|
|
+ @classmethod
|
|
+ def uuid_format_ok(cls, uuid):
|
|
+ if len(uuid) != 16:
|
|
+ return False
|
|
+ return all(char in "0123456789ABCDEF" for char in uuid)
|
|
diff --git a/blivet/tasks/fswriteuuid.py b/blivet/tasks/fswriteuuid.py
|
|
new file mode 100644
|
|
index 00000000..5bc54ba1
|
|
--- /dev/null
|
|
+++ b/blivet/tasks/fswriteuuid.py
|
|
@@ -0,0 +1,93 @@
|
|
+import abc
|
|
+
|
|
+from six import add_metaclass
|
|
+
|
|
+from .. import util
|
|
+from ..errors import FSWriteUUIDError
|
|
+
|
|
+from . import availability
|
|
+from . import fstask
|
|
+from . import task
|
|
+
|
|
+
|
|
+@add_metaclass(abc.ABCMeta)
|
|
+class FSWriteUUID(task.BasicApplication, fstask.FSTask):
|
|
+
|
|
+ """ An abstract class that represents writing an UUID for a filesystem. """
|
|
+
|
|
+ description = "write filesystem UUID"
|
|
+
|
|
+ args = abc.abstractproperty(doc="arguments for writing a UUID")
|
|
+
|
|
+ # IMPLEMENTATION methods
|
|
+
|
|
+ @property
|
|
+ def _set_command(self):
|
|
+ """Get the command to set UUID of the filesystem.
|
|
+
|
|
+ :return: the command
|
|
+ :rtype: list of str
|
|
+ """
|
|
+ return [str(self.ext)] + self.args
|
|
+
|
|
+ def do_task(self):
|
|
+ error_msgs = self.availability_errors
|
|
+ if error_msgs:
|
|
+ raise FSWriteUUIDError("\n".join(error_msgs))
|
|
+
|
|
+ rc = util.run_program(self._set_command)
|
|
+ if rc:
|
|
+ msg = "setting UUID via {} failed".format(self._set_command)
|
|
+ raise FSWriteUUIDError(msg)
|
|
+
|
|
+
|
|
+class DosFSWriteUUID(FSWriteUUID):
|
|
+ ext = availability.MLABEL_APP
|
|
+
|
|
+ @property
|
|
+ def args(self):
|
|
+ return ["-N", self.fs.uuid, "-i", self.fs.device, "::"]
|
|
+
|
|
+
|
|
+class Ext2FSWriteUUID(FSWriteUUID):
|
|
+ ext = availability.TUNE2FS_APP
|
|
+
|
|
+ @property
|
|
+ def args(self):
|
|
+ return ["-U", self.fs.uuid, self.fs.device]
|
|
+
|
|
+
|
|
+class JFSWriteUUID(FSWriteUUID):
|
|
+ ext = availability.JFSTUNE_APP
|
|
+
|
|
+ @property
|
|
+ def args(self):
|
|
+ return ["-U", self.fs.uuid, self.fs.device]
|
|
+
|
|
+
|
|
+class NTFSWriteUUID(FSWriteUUID):
|
|
+ ext = availability.NTFSLABEL_APP
|
|
+
|
|
+ @property
|
|
+ def args(self):
|
|
+ return ["--new-serial=" + self.fs.uuid, self.fs.device]
|
|
+
|
|
+
|
|
+class ReiserFSWriteUUID(FSWriteUUID):
|
|
+ ext = availability.REISERFSTUNE_APP
|
|
+
|
|
+ @property
|
|
+ def args(self):
|
|
+ return ["-u", self.fs.uuid, self.fs.device]
|
|
+
|
|
+
|
|
+class XFSWriteUUID(FSWriteUUID):
|
|
+ ext = availability.XFSADMIN_APP
|
|
+
|
|
+ @property
|
|
+ def args(self):
|
|
+ return ["-U", self.fs.uuid, self.fs.device]
|
|
+
|
|
+
|
|
+class UnimplementedFSWriteUUID(fstask.UnimplementedFSTask):
|
|
+ pass
|
|
diff --git a/tests/formats_test/fsuuid.py b/tests/formats_test/fsuuid.py
|
|
new file mode 100644
|
|
index 00000000..0b550cbc
|
|
--- /dev/null
|
|
+++ b/tests/formats_test/fsuuid.py
|
|
@@ -0,0 +1,111 @@
|
|
+import sys
|
|
+import abc
|
|
+from six import add_metaclass
|
|
+from unittest import skipIf
|
|
+
|
|
+from tests import loopbackedtestcase
|
|
+from blivet.devicetree import DeviceTree
|
|
+from blivet.errors import FSError, FSWriteUUIDError
|
|
+from blivet.size import Size
|
|
+
|
|
+
|
|
+@add_metaclass(abc.ABCMeta)
|
|
+class SetUUID(loopbackedtestcase.LoopBackedTestCase):
|
|
+
|
|
+ """Base class for UUID tests without any test methods."""
|
|
+
|
|
+ _fs_class = abc.abstractproperty(
|
|
+ doc="The class of the filesystem being tested on.")
|
|
+
|
|
+ _valid_uuid = abc.abstractproperty(
|
|
+ doc="A valid UUID for this filesystem.")
|
|
+
|
|
+ _invalid_uuid = abc.abstractproperty(
|
|
+ doc="An invalid UUID for this filesystem.")
|
|
+
|
|
+ def __init__(self, methodName='run_test'):
|
|
+ super(SetUUID, self).__init__(methodName=methodName,
|
|
+ device_spec=[Size("100 MiB")])
|
|
+
|
|
+ def setUp(self):
|
|
+ an_fs = self._fs_class()
|
|
+ if not an_fs.formattable:
|
|
+ self.skipTest("can not create filesystem %s" % an_fs.name)
|
|
+ super(SetUUID, self).setUp()
|
|
+
|
|
+
|
|
+class SetUUIDWithMkFs(SetUUID):
|
|
+
|
|
+ """Tests various aspects of setting an UUID for a filesystem where the
|
|
+ native mkfs tool can set the UUID.
|
|
+ """
|
|
+
|
|
+ @skipIf(sys.version_info < (3, 4), "assertLogs is not supported")
|
|
+ def test_set_invalid_uuid(self):
|
|
+ """Create the filesystem with an invalid UUID."""
|
|
+ an_fs = self._fs_class(device=self.loop_devices[0],
|
|
+ uuid=self._invalid_uuid)
|
|
+ if self._fs_class._type == "swap":
|
|
+ with self.assertRaisesRegex(FSWriteUUIDError, "bad UUID format"):
|
|
+ an_fs.create()
|
|
+ else:
|
|
+ with self.assertLogs('blivet', 'WARNING') as logs:
|
|
+ an_fs.create()
|
|
+ self.assertTrue(len(logs.output) >= 1)
|
|
+ self.assertRegex(logs.output[0], "UUID format.*unacceptable")
|
|
+
|
|
+ def test_set_uuid(self):
|
|
+ """Create the filesystem with a valid UUID."""
|
|
+ an_fs = self._fs_class(device=self.loop_devices[0],
|
|
+ uuid=self._valid_uuid)
|
|
+ self.assertIsNone(an_fs.create())
|
|
+
|
|
+ # Use DeviceTree for detecting the loop device and check whether the
|
|
+ # UUID is correct.
|
|
+ dt = DeviceTree()
|
|
+ dt.reset()
|
|
+ dt.populate()
|
|
+ device = dt.get_device_by_path(self.loop_devices[0])
|
|
+ self.assertEqual(device.format.uuid, self._valid_uuid)
|
|
+
|
|
+
|
|
+class SetUUIDAfterMkFs(SetUUID):
|
|
+
|
|
+ """Tests various aspects of setting an UUID for a filesystem where the
|
|
+ native mkfs tool can't set the UUID.
|
|
+ """
|
|
+
|
|
+ def setUp(self):
|
|
+ an_fs = self._fs_class()
|
|
+ if an_fs._writeuuid.availability_errors:
|
|
+ self.skipTest("can not write UUID for filesystem %s" % an_fs.name)
|
|
+ super(SetUUIDAfterMkFs, self).setUp()
|
|
+
|
|
+ def test_set_uuid_later(self):
|
|
+ """Create the filesystem with random UUID and reassign later."""
|
|
+ an_fs = self._fs_class(device=self.loop_devices[0])
|
|
+ if an_fs._writeuuid.availability_errors:
|
|
+ self.skipTest("can not write UUID for filesystem %s" % an_fs.name)
|
|
+ self.assertIsNone(an_fs.create())
|
|
+
|
|
+ an_fs.uuid = self._valid_uuid
|
|
+ self.assertIsNone(an_fs.write_uuid())
|
|
+
|
|
+ # Use DeviceTree for detecting the loop device and check whether the
|
|
+ # UUID is correct.
|
|
+ dt = DeviceTree()
|
|
+ dt.reset()
|
|
+ dt.populate()
|
|
+ device = dt.get_device_by_path(self.loop_devices[0])
|
|
+ self.assertEqual(device.format.uuid, self._valid_uuid)
|
|
+
|
|
+ def test_set_invalid_uuid_later(self):
|
|
+ """Create the filesystem and try to reassign an invalid UUID later."""
|
|
+ an_fs = self._fs_class(device=self.loop_devices[0])
|
|
+ if an_fs._writeuuid.availability_errors:
|
|
+ self.skipTest("can not write UUID for filesystem %s" % an_fs.name)
|
|
+ self.assertIsNone(an_fs.create())
|
|
+
|
|
+ an_fs.uuid = self._invalid_uuid
|
|
+ with self.assertRaisesRegex(FSError, "bad UUID format"):
|
|
+ an_fs.write_uuid()
|
|
diff --git a/tests/formats_test/methods_test.py b/tests/formats_test/methods_test.py
|
|
index 9df74158..5659216c 100644
|
|
--- a/tests/formats_test/methods_test.py
|
|
+++ b/tests/formats_test/methods_test.py
|
|
@@ -300,7 +300,12 @@ class FSMethodsTestCase(FormatMethodsTestCase):
|
|
with patch.object(self.format, "_mkfs"):
|
|
self.format.exists = False
|
|
self.format.create()
|
|
- self.format._mkfs.do_task.assert_called_with(options=None, label=not self.format.relabels()) # pylint: disable=no-member
|
|
+ # pylint: disable=no-member
|
|
+ self.format._mkfs.do_task.assert_called_with(
|
|
+ options=None,
|
|
+ label=not self.format.relabels(),
|
|
+ set_uuid=self.format.can_assign_uuid()
|
|
+ )
|
|
|
|
def _test_setup_backend(self):
|
|
with patch.object(self.format, "_mount"):
|
|
diff --git a/tests/formats_test/uuid_test.py b/tests/formats_test/uuid_test.py
|
|
new file mode 100644
|
|
index 00000000..1ed40386
|
|
--- /dev/null
|
|
+++ b/tests/formats_test/uuid_test.py
|
|
@@ -0,0 +1,87 @@
|
|
+import unittest
|
|
+
|
|
+import blivet.formats.fs as fs
|
|
+import blivet.formats.swap as swap
|
|
+
|
|
+from . import fsuuid
|
|
+
|
|
+
|
|
+class InitializationTestCase(unittest.TestCase):
|
|
+
|
|
+ """Test FS object initialization."""
|
|
+
|
|
+ def test_uuids(self):
|
|
+ """Initialize some filesystems with valid and invalid UUIDs."""
|
|
+
|
|
+ # File systems that accept real UUIDs (RFC 4122)
|
|
+ for fscls in [fs.Ext2FS, fs.JFS, fs.ReiserFS, fs.XFS, fs.HFSPlus]:
|
|
+ uuid = "0invalid-uuid-with-righ-tlength00000"
|
|
+ self.assertFalse(fscls().uuid_format_ok(uuid))
|
|
+ uuid = "01234567-12341234123401234567891a"
|
|
+ self.assertFalse(fscls().uuid_format_ok(uuid))
|
|
+ uuid = "0123456-123-123-123-01234567891"
|
|
+ self.assertFalse(fscls().uuid_format_ok(uuid))
|
|
+ uuid = "01234567-xyz-1234-1234-1234-012345678911"
|
|
+ self.assertFalse(fscls().uuid_format_ok(uuid))
|
|
+ uuid = "01234567-1234-1234-1234-012345678911"
|
|
+ self.assertTrue(fscls().uuid_format_ok(uuid))
|
|
+
|
|
+ self.assertFalse(fs.FATFS().uuid_format_ok("1234-56789"))
|
|
+ self.assertFalse(fs.FATFS().uuid_format_ok("abcd-ef00"))
|
|
+ self.assertFalse(fs.FATFS().uuid_format_ok("12345678"))
|
|
+ self.assertTrue(fs.FATFS().uuid_format_ok("1234-5678"))
|
|
+ self.assertTrue(fs.FATFS().uuid_format_ok("ABCD-EF01"))
|
|
+
|
|
+ self.assertFalse(fs.NTFS().uuid_format_ok("12345678901234567"))
|
|
+ self.assertFalse(fs.NTFS().uuid_format_ok("abcdefgh"))
|
|
+ self.assertFalse(fs.NTFS().uuid_format_ok("abcdefabcdefabcd"))
|
|
+ self.assertTrue(fs.NTFS().uuid_format_ok("1234567890123456"))
|
|
+ self.assertTrue(fs.NTFS().uuid_format_ok("ABCDEFABCDEFABCD"))
|
|
+
|
|
+
|
|
+class XFSTestCase(fsuuid.SetUUIDWithMkFs):
|
|
+ _fs_class = fs.XFS
|
|
+ _invalid_uuid = "abcdefgh-ijkl-mnop-qrst-uvwxyz123456"
|
|
+ _valid_uuid = "97e3d40f-dca8-497d-8b86-92f257402465"
|
|
+
|
|
+
|
|
+class FATFSTestCase(fsuuid.SetUUIDWithMkFs):
|
|
+ _fs_class = fs.FATFS
|
|
+ _invalid_uuid = "c87ab0e1"
|
|
+ _valid_uuid = "DEAD-BEEF"
|
|
+
|
|
+
|
|
+class Ext2FSTestCase(fsuuid.SetUUIDWithMkFs):
|
|
+ _fs_class = fs.Ext2FS
|
|
+ _invalid_uuid = "abcdefgh-ijkl-mnop-qrst-uvwxyz123456"
|
|
+ _valid_uuid = "bad19a10-075a-4e99-8922-e4638722a567"
|
|
+
|
|
+
|
|
+class JFSTestCase(fsuuid.SetUUIDAfterMkFs):
|
|
+ _fs_class = fs.JFS
|
|
+ _invalid_uuid = "abcdefgh-ijkl-mnop-qrst-uvwxyz123456"
|
|
+ _valid_uuid = "ac54f987-b371-45d9-8846-7d6204081e5c"
|
|
+
|
|
+
|
|
+class ReiserFSTestCase(fsuuid.SetUUIDWithMkFs):
|
|
+ _fs_class = fs.ReiserFS
|
|
+ _invalid_uuid = "abcdefgh-ijkl-mnop-qrst-uvwxyz123456"
|
|
+ _valid_uuid = "1761023e-bab8-4919-a2cb-f26c89fe1cfe"
|
|
+
|
|
+
|
|
+class HFSPlusTestCase(fsuuid.SetUUIDAfterMkFs):
|
|
+ _fs_class = fs.HFSPlus
|
|
+ _invalid_uuid = "abcdefgh-ijkl-mnop-qrst-uvwxyz123456"
|
|
+ _valid_uuid = "3e6d84ce-cca9-4f55-9950-59e5b31f0e36"
|
|
+
|
|
+
|
|
+class NTFSTestCase(fsuuid.SetUUIDAfterMkFs):
|
|
+ _fs_class = fs.NTFS
|
|
+ _invalid_uuid = "b22193477ac947fb"
|
|
+ _valid_uuid = "BC3B34461B8344A6"
|
|
+
|
|
+
|
|
+class SwapSpaceTestCase(fsuuid.SetUUIDWithMkFs):
|
|
+ _fs_class = swap.SwapSpace
|
|
+ _invalid_uuid = "abcdefgh-ijkl-mnop-qrst-uvwxyz123456"
|
|
+ _valid_uuid = "01234567-1234-1234-1234-012345678912"
|