# Copyright (c) 2023 Adolfo Gómez
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""
This module contains Filesystem related classes and functions
:author: Adolfo Gómez, dkmaster at dkmon dot com
"""
from os import stat
import typing
import enum
import logging
from parted.exceptions import PartedException
from . import _parted # type: ignore
from . import geom
from .util import make_destroyable
if typing.TYPE_CHECKING:
import cffi
from . import timer
logger = logging.getLogger(__name__)
[docs]class FileSystemType:
"""This class represents a FileSystem
"""
_filesystemtype: typing.Any = None
# Well know filesystems, some of them, all supported by parted
[docs] class WNT(enum.Enum):
"""Well known filesystems identifiers in parted
This class contains the well known filesystems identifiers in parted.
"""
zfs = 'zfs'
udf = 'udf'
btrfs = 'btrfs'
nilfs2 = 'nilfs2'
ext2 = 'ext2'
ext3 = 'ext3'
ext4 = 'ext4'
fat16 = 'fat16'
fat32 = 'fat32'
hfs = 'hfs'
hfsplus = 'hfs+'
hfsx = 'hfsx'
jfs = 'jfs'
swsusp = 'swsusp'
linux_swap = 'linux-swap'
linux_swap_v0 = 'linux-swap(v0)'
linux_swap_v1 = 'linux-swap(v1)'
ntfs = 'ntfs'
reiserfs = 'reiserfs'
freebsd_ufs = 'freebsd-ufs'
hp_ufs = 'hp-ufs'
sun_ufs = 'sun-ufs'
xfs = 'xfs'
apfs1 = 'apfs1'
apfs2 = 'apfs2'
asfs = 'asfs'
amufs0 = 'amufs0'
amufs1 = 'amufs1'
amufs2 = 'amufs2'
amufs3 = 'amufs3'
amufs4 = 'amufs4'
amufs5 = 'amufs5'
affs0 = 'affs0'
affs1 = 'affs1'
affs2 = 'affs2'
affs3 = 'affs3'
affs4 = 'affs4'
affs5 = 'affs5'
affs6 = 'affs6'
[docs] @staticmethod
def from_string(name: str) -> 'FileSystemType.WNT':
"""Returns a well known filesystem type from its name
Args:
name (str): Name of the filesystem type
Returns:
FileSystemType.WNT: The filesystem type
Raises:
ValueError: If the filesystem type is not known
"""
return FileSystemType.WNT.__members__[name]
def __str__(self):
return self.value
def __init__(self, filesystemtype: typing.Union[WNT, str, 'cffi.FFI.CData', None] = None) -> None:
"""Creates a new FileSystemType object
Args:
filesystemtype (typing.Union[WNF, str, cffi.FFI.CData], optional): The filesystem type to create. Defaults to None.
If None, a "nil" filesystem type is created, which is not valid, but can be used to iterate over all filesystem types
or to compare with other filesystem types.
"""
if isinstance(filesystemtype, (str, FileSystemType.WNT)):
fst = str(filesystemtype)
self._filesystemtype = _parted.lib.ped_file_system_type_get(fst.encode())
else:
self._filesystemtype = filesystemtype if filesystemtype else _parted.ffi.NULL
def __bool__(self) -> bool:
return bool(self._filesystemtype)
def __eq__(self, other: typing.Any) -> bool:
"""Compares against another FileSystemType, string or WNF
Args:
other (typing.Any): The other object to compare against
Returns:
bool: True if both are related to same Filesystem, False otherwise
"""
if isinstance(other, FileSystemType):
return self._filesystemtype == other._filesystemtype
elif isinstance(other, FileSystemType.WNT):
return self.name == other.value
elif isinstance(other, str):
return self.name == other
else:
return False
@property
def obj(self) -> 'cffi.FFI.CData':
"""Wrapped ``PedFileSystemType*`` object"""
return self._filesystemtype
@property
def name(self) -> str:
"""Name of the filesystem (i.e. ext4, fat32, etc)"""
if not self._filesystemtype:
return ''
return _parted.ffi.string(self._filesystemtype.name).decode()
[docs] def next(self) -> 'FileSystemType':
"""Returns the next filesystem type
Returns:
FileSystemType: The next filesystem type
"""
fs = self._filesystemtype if self._filesystemtype else _parted.ffi.NULL
return FileSystemType(_parted.lib.ped_file_system_type_get_next(fs))
[docs] @staticmethod
def enumerate() -> typing.Iterator['FileSystemType']:
"""Enumerates all filesystem types
Yields:
FileSystemType: Available valid filesystem types
"""
fs = _parted.ffi.NULL
while True:
fs = _parted.lib.ped_file_system_type_get_next(fs)
if not fs:
break
yield FileSystemType(fs)
[docs] @staticmethod
def from_string(name: str) -> 'FileSystemType':
"""Returns a filesystem type from its name
Args:
name (str): Name of the filesystem type
Returns:
FileSystemType: The filesystem type
"""
return FileSystemType(name)
[docs] @staticmethod
def none() -> 'FileSystemType':
"""Returns the "nil" filesystem type
Returns:
FileSystemType: The "nil" filesystem type
"""
return FileSystemType()
def __str__(self) -> str:
return self.name
[docs]class FileSystem:
"""This class represents a FileSystem"""
_filesystem: typing.Any
def __init__(self, filesystem: typing.Optional['cffi.FFI.CData'] = None):
"""Creates a new FileSystem object
Args:
filesystem (cffi.FFI.CData, optional): The filesystem to create. Defaults to None.
If None, a "nil" filesystem is created, which is not valid, but can be used to iterate over all filesystems
or to compare with other filesystems.
"""
self._filesystem = filesystem if filesystem else _parted.ffi.NULL
def __bool__(self) -> bool:
return bool(self._filesystem)
@property
def obj(self) -> 'cffi.FFI.CData':
"""Wrapped ``PedFileSystem*`` object"""
return self._filesystem
@property
def geometry(self) -> 'geom.Geometry':
"""Geometry of the filesystem"""
return geom.Geometry(self._filesystem.geom)
@property
def type(self) -> 'FileSystemType':
"""Type of the filesystem"""
return FileSystemType(self._filesystem.type)
@property
def checked(self) -> bool:
"""Whether the filesystem has been checked"""
return bool(self._filesystem.checked)
[docs] @staticmethod
def probe(gometry: 'geom.Geometry') -> 'FileSystemType':
"""Probes the file system on the given geometry.
Args:
geometry (geom.Geometry): The geometry to probe
Returns:
FileSystemType: The filesystem type. If no filesystem is found, returns "nil" FileSystemType
"""
if not gometry:
raise PartedException('Invalid geometry')
return FileSystemType(_parted.lib.ped_file_system_probe(gometry.obj))
[docs] @staticmethod
def probe_specific(
gometry: 'geom.Geometry', fstype: typing.Union['FileSystemType.WNT', 'FileSystemType']
) -> 'geom.Geometry':
"""Probes the file system on the given geometry.
Args:
geometry (geom.Geometry): The geometry to probe
fstype (typing.Union[FileSystemType.WNT, FileSystemType]): The filesystem type to probe
Returns:
geom.Geometry: The geometry of the filesystem. If no filesystem is found, returns "nil" Geometry
"""
if isinstance(fstype, FileSystemType.WNT):
fstype = FileSystemType(fstype)
return make_destroyable(geom.Geometry(_parted.lib.ped_file_system_probe_specific(fstype.obj, gometry.obj)))