Source code for cobbler.utils.mtab

"""
We cache the contents of ``/etc/mtab``. The following module is used to keep our cache in sync.
"""

import os
from typing import Any, Dict, List, Optional, Tuple

MTAB_MTIME = None
MTAB_MAP = []


[docs]class MntEntObj: """ TODO """ mnt_fsname = None # name of mounted file system mnt_dir = None # file system path prefix mnt_type = None # mount type (see mntent.h) mnt_opts = None # mount options (see mntent.h) mnt_freq = 0 # dump frequency in days mnt_passno = 0 # pass number on parallel fsck def __init__(self, input_data: Optional[str] = None): """ This is an object which contains information about a mounted filesystem. :param input_data: This is a string which is separated internally by whitespace. If present it represents the arguments: "mnt_fsname", "mnt_dir", "mnt_type", "mnt_opts", "mnt_freq" and "mnt_passno". The order must be preserved, as well as the separation by whitespace. """ if input_data and isinstance(input_data, str): # type: ignore split_data = input_data.split() self.mnt_fsname = split_data[0] self.mnt_dir = split_data[1] self.mnt_type = split_data[2] self.mnt_opts = split_data[3] self.mnt_freq = int(split_data[4]) self.mnt_passno = int(split_data[5]) def __dict__(self) -> Dict[str, Any]: # type: ignore """ This maps all variables available in this class to a dictionary. The name of the keys is identical to the names of the variables. :return: The dictionary representation of an instance of this class. """ # See https://github.com/python/mypy/issues/6523 why this is ignored return { "mnt_fsname": self.mnt_fsname, "mnt_dir": self.mnt_dir, "mnt_type": self.mnt_type, "mnt_opts": self.mnt_opts, "mnt_freq": self.mnt_freq, "mnt_passno": self.mnt_passno, } def __str__(self) -> str: """ This is the object representation of a mounted filesystem as a string. It can be fed to the constructor of this class. :return: The space separated list of values of this object. """ return f"{self.mnt_fsname} {self.mnt_dir} {self.mnt_type} {self.mnt_opts} {self.mnt_freq} {self.mnt_passno}"
[docs]def get_mtab(mtab: str = "/etc/mtab", vfstype: bool = False) -> List[MntEntObj]: """ Get the list of mtab entries. If a custom mtab should be read then the location can be overridden via a parameter. :param mtab: The location of the mtab. Argument can be omitted if the mtab is at its default location. :param vfstype: If this is True, then all filesystems which are nfs are returned. Otherwise this returns all mtab entries. :return: The list of requested mtab entries. """ # These two variables are required to be caches on the module level to be persistent during runtime. global MTAB_MTIME, MTAB_MAP # pylint: disable=global-statement mtab_stat = os.stat(mtab) if mtab_stat.st_mtime != MTAB_MTIME: # type: ignore # cache is stale ... refresh MTAB_MTIME = mtab_stat.st_mtime # type: ignore MTAB_MAP = __cache_mtab__(mtab) # type: ignore # was a specific fstype requested? if vfstype: mtab_type_map: List[MntEntObj] = [] for ent in MTAB_MAP: if ent.mnt_type == "nfs": mtab_type_map.append(ent) return mtab_type_map return MTAB_MAP
def __cache_mtab__(mtab: str = "/etc/mtab") -> List[MntEntObj]: """ Open the mtab and cache it inside Cobbler. If it is guessed that the mtab hasn't changed the cache data is used. :param mtab: The location of the mtab. Argument can be ommited if the mtab is at its default location. :return: The mtab content stripped from empty lines (if any are present). """ with open(mtab, encoding="UTF-8") as mtab_fd: result = [ MntEntObj(line) for line in mtab_fd.read().split("\n") if len(line) > 0 ] return result
[docs]def get_file_device_path(fname: str) -> Tuple[Optional[str], str]: """ What this function attempts to do is take a file and return: - the device the file is on - the path of the file relative to the device. For example: /boot/vmlinuz -> (/dev/sda3, /vmlinuz) /boot/efi/efi/redhat/elilo.conf -> (/dev/cciss0, /elilo.conf) /etc/fstab -> (/dev/sda4, /etc/fstab) :param fname: The filename to split up. :return: A tuple containing the device and relative filename. """ # resolve any symlinks fname = os.path.realpath(fname) # convert mtab to a dict mtab_dict: Dict[str, str] = {} try: for ent in get_mtab(): mtab_dict[ent.mnt_dir] = ent.mnt_fsname # type: ignore except Exception: pass # find a best match fdir = os.path.dirname(fname) match = fdir in mtab_dict chrootfs = False while not match: if fdir == os.path.sep: chrootfs = True break fdir = os.path.realpath(os.path.join(fdir, os.path.pardir)) match = fdir in mtab_dict # construct file path relative to device if fdir != os.path.sep: fname = fname[len(fdir) :] if chrootfs: return ":", fname return mtab_dict[fdir], fname
[docs]def is_remote_file(file: str) -> bool: """ This function is trying to detect if the file in the argument is remote or not. :param file: The filepath to check. :return: If remote True, otherwise False. """ (dev, _) = get_file_device_path(file) if dev is None: return False if dev.find(":") != -1: return True return False