diff options
Diffstat (limited to 'pyanaconda/storage/formats/fs.py')
-rw-r--r-- | pyanaconda/storage/formats/fs.py | 1433 |
1 files changed, 0 insertions, 1433 deletions
diff --git a/pyanaconda/storage/formats/fs.py b/pyanaconda/storage/formats/fs.py deleted file mode 100644 index b472e8727..000000000 --- a/pyanaconda/storage/formats/fs.py +++ /dev/null @@ -1,1433 +0,0 @@ -# filesystems.py -# Filesystem classes for anaconda's storage configuration module. -# -# Copyright (C) 2009 Red Hat, Inc. -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions of -# the GNU General Public License v.2, or (at your option) any later version. -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY expressed or implied, including the implied warranties of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. You should have received a copy of the -# GNU General Public License along with this program; if not, write to the -# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the -# source code or documentation are not subject to the GNU General Public -# License and may only be used or replicated with the express permission of -# Red Hat, Inc. -# -# Red Hat Author(s): Dave Lehman <dlehman@redhat.com> -# David Cantrell <dcantrell@redhat.com> -# - -""" Filesystem classes for use by anaconda. - - TODO: - - bug 472127: allow creation of tmpfs filesystems (/tmp, /var/tmp, &c) -""" -import math -import os -import sys -import tempfile -import selinux - -from ..errors import * -from . import DeviceFormat, register_device_format -from .. import util -from .. import platform -from ..flags import flags -from parted import fileSystemType -from ..storage_log import log_method_call - -import logging -log = logging.getLogger("storage") - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -try: - lost_and_found_context = selinux.matchpathcon("/lost+found", 0)[1] -except OSError: - lost_and_found_context = None - -# these are for converting to/from SI for ntfsresize -mb = 1000 * 1000.0 -mib = 1024 * 1024.0 - -fs_configs = {} - -def get_kernel_filesystems(): - fs_list = [] - for line in open("/proc/filesystems").readlines(): - fs_list.append(line.split()[-1]) - return fs_list - -global kernel_filesystems -kernel_filesystems = get_kernel_filesystems() - -def fsConfigFromFile(config_file): - """ Generate a set of attribute name/value pairs with which a - filesystem type can be defined. - - The following config file would define a filesystem identical to - the static Ext3FS class definition: - - type = ext3 - mkfs = "mke2fs" - resizefs = "resize2fs" - labelfs = "e2label" - fsck = "e2fsck" - packages = ["e2fsprogs"] - formattable = True - supported = True - resizable = True - linuxNative = True - maxSize = 8 * 1024 * 1024 - minSize = 0 - defaultFormatOptions = "-t ext3" - defaultMountOptions = "defaults" - - """ - # XXX NOTUSED - lines = open(config_file).readlines() - fs_attrs = {} - for line in lines: - (key, value) = [t.strip() for t in line.split("=")] - if not hasattr(FS, "_" + key): - print "invalid key: %s" % key - continue - - fs_attrs[key] = value - - if not fs_attrs.has_key("type"): - raise ValueError, _("filesystem configuration missing a type") - - # XXX what's the policy about multiple configs for a given type? - fs_configs[fs_attrs['type']] = fs_attrs - -class FS(DeviceFormat): - """ Filesystem class. """ - _type = "Abstract Filesystem Class" # fs type name - _mountType = None # like _type but for passing to mount - _name = None - _mkfs = "" # mkfs utility - _modules = [] # kernel modules required for support - _resizefs = "" # resize utility - _labelfs = "" # labeling utility - _fsck = "" # fs check utility - _fsckErrors = {} # fs check command error codes & msgs - _infofs = "" # fs info utility - _defaultFormatOptions = [] # default options passed to mkfs - _defaultMountOptions = ["defaults"] # default options passed to mount - _defaultLabelOptions = [] - _defaultCheckOptions = [] - _defaultInfoOptions = [] - _existingSizeFields = [] - _fsProfileSpecifier = None # mkfs option specifying fsprofile - - def __init__(self, *args, **kwargs): - """ Create a FS instance. - - Keyword Args: - - device -- path to the device containing the filesystem - mountpoint -- the filesystem's mountpoint - label -- the filesystem label - uuid -- the filesystem UUID - mountopts -- mount options for the filesystem - size -- the filesystem's size in MiB - exists -- indicates whether this is an existing filesystem - - """ - if self.__class__ is FS: - raise TypeError("FS is an abstract class.") - - DeviceFormat.__init__(self, *args, **kwargs) - self.mountpoint = kwargs.get("mountpoint") - self.mountopts = kwargs.get("mountopts") - self.label = kwargs.get("label") - self.fsprofile = kwargs.get("fsprofile") - - # filesystem size does not necessarily equal device size - self._size = kwargs.get("size", 0) - self._minInstanceSize = None # min size of this FS instance - self._mountpoint = None # the current mountpoint when mounted - if self.exists: - self._size = self._getExistingSize() - foo = self.minSize # force calculation of minimum size - - self._targetSize = self._size - - if self.supported: - self.loadModule() - - def __repr__(self): - s = DeviceFormat.__repr__(self) - s += (" mountpoint = %(mountpoint)s mountopts = %(mountopts)s\n" - " label = %(label)s size = %(size)s" - " targetSize = %(targetSize)s\n" % - {"mountpoint": self.mountpoint, "mountopts": self.mountopts, - "label": self.label, "size": self._size, - "targetSize": self.targetSize}) - return s - - @property - def desc(self): - s = "%s filesystem" % self.type - if self.mountpoint: - s += " mounted at %s" % self.mountpoint - return s - - @property - def dict(self): - d = super(FS, self).dict - d.update({"mountpoint": self.mountpoint, "size": self._size, - "label": self.label, "targetSize": self.targetSize, - "mountable": self.mountable}) - return d - - def _setTargetSize(self, newsize): - """ Set a target size for this filesystem. """ - if not self.exists: - raise FSError("filesystem has not been created") - - if newsize is None: - # unset any outstanding resize request - self._targetSize = self._size - return - - if not self.minSize <= newsize < self.maxSize: - raise ValueError("invalid target size request") - - self._targetSize = newsize - - def _getTargetSize(self): - """ Get this filesystem's target size. """ - return self._targetSize - - targetSize = property(_getTargetSize, _setTargetSize, - doc="Target size for this filesystem") - - def _getSize(self): - """ Get this filesystem's size. """ - size = self._size - if self.resizable and self.targetSize != size: - size = self.targetSize - return size - - size = property(_getSize, doc="This filesystem's size, accounting " - "for pending changes") - - def _getExistingSize(self): - """ Determine the size of this filesystem. Filesystem must - exist. Each filesystem varies, but the general procedure - is to run the filesystem dump or info utility and read - the block size and number of blocks for the filesystem - and compute megabytes from that. - - The loop that reads the output from the infofsProg is meant - to be simple, but take in to account variations in output. - The general procedure: - 1) Capture output from infofsProg. - 2) Iterate over each line of the output: - a) Trim leading and trailing whitespace. - b) Break line into fields split on ' ' - c) If line begins with any of the strings in - _existingSizeFields, start at the end of - fields and take the first one that converts - to a long. Store this in the values list. - d) Repeat until the values list length equals - the _existingSizeFields length. - 3) If the length of the values list equals the length - of _existingSizeFields, compute the size of this - filesystem by multiplying all of the values together - to get bytes, then convert to megabytes. Return - this value. - 4) If we were unable to capture all fields, return 0. - - The caller should catch exceptions from this method. Any - exception raised indicates a need to change the fields we - are looking for, the command to run and arguments, or - something else. If you catch an exception from this method, - assume the filesystem cannot be resized. - """ - size = self._size - - if self.infofsProg and self.exists and not size and \ - util.find_program_in_path(self.infofsProg): - try: - values = [] - argv = self._defaultInfoOptions + [ self.device ] - - buf = util.capture_output([self.infofsProg] + argv) - - for line in buf.splitlines(): - found = False - - line = line.strip() - tmp = line.split(' ') - tmp.reverse() - - for field in self._existingSizeFields: - if line.startswith(field): - for subfield in tmp: - try: - values.append(long(subfield)) - found = True - break - except ValueError: - continue - - if found: - break - - if len(values) == len(self._existingSizeFields): - break - - if len(values) != len(self._existingSizeFields): - return 0 - - size = 1 - for value in values: - size *= value - - # report current size as megabytes - size = math.floor(size / 1024.0 / 1024.0) - except Exception as e: - log.error("failed to obtain size of filesystem on %s: %s" - % (self.device, e)) - - return size - - @property - def currentSize(self): - """ The filesystem's current actual size. """ - size = 0 - if self.exists: - size = self._size - return float(size) - - @property - def free(self): - free = 0 - if self.exists: - if self.currentSize and self.minSize and \ - self.currentSize != self.minSize: - free = int(max(0, self.currentSize - self.minSize)) # truncate - - return free - - def _getFormatOptions(self, options=None): - argv = [] - if options and isinstance(options, list): - argv.extend(options) - argv.extend(self.defaultFormatOptions) - if self._fsProfileSpecifier and self.fsprofile: - argv.extend([self._fsProfileSpecifier, self.fsprofile]) - argv.append(self.device) - return argv - - def doFormat(self, *args, **kwargs): - """ Create the filesystem. - - Arguments: - - None - - Keyword Arguments: - - options -- list of options to pass to mkfs - - """ - log_method_call(self, type=self.mountType, device=self.device, - mountpoint=self.mountpoint) - - options = kwargs.get("options") - - if self.exists: - raise FormatCreateError("filesystem already exists", self.device) - - if not self.formattable: - return - - if not self.mkfsProg: - return - - if self.exists: - return - - if not os.path.exists(self.device): - raise FormatCreateError("device does not exist", self.device) - - argv = self._getFormatOptions(options=options) - - try: - ret = util.run_program([self.mkfsProg] + argv) - except OSError as e: - raise FormatCreateError(e, self.device) - - if ret: - raise FormatCreateError("format failed: %s" % ret.rc, self.device) - - self.exists = True - self.notifyKernel() - - if self.label: - self.writeLabel(self.label) - - @property - def resizeArgs(self): - argv = [self.device, "%d" % (self.targetSize,)] - return argv - - def doResize(self, *args, **kwargs): - """ Resize this filesystem to new size @newsize. - - Arguments: - - None - """ - - if not self.exists: - raise FSResizeError("filesystem does not exist", self.device) - - if not self.resizable: - raise FSResizeError("filesystem not resizable", self.device) - - if self.targetSize == self.currentSize: - return - - if not self.resizefsProg: - return - - if not os.path.exists(self.device): - raise FSResizeError("device does not exist", self.device) - - self.doCheck() - - # The first minimum size can be incorrect if the fs was not - # properly unmounted. After doCheck the minimum size will be correct - # so run the check one last time and bump up the size if it was too - # small. - self._minInstanceSize = None - if self.targetSize < self.minSize: - self.targetSize = self.minSize - log.info("Minimum size changed, setting targetSize on %s to %s" \ - % (self.device, self.targetSize)) - try: - ret = util.run_program([self.resizefsProg] + self.resizeArgs) - except OSError as e: - raise FSResizeError(e, self.device) - - if ret: - raise FSResizeError("resize failed: %s" % ret, self.device) - - self.doCheck() - - # XXX must be a smarter way to do this - self._size = self.targetSize - self.notifyKernel() - - def _getCheckArgs(self): - argv = [] - argv.extend(self.defaultCheckOptions) - argv.append(self.device) - return argv - - def _fsckFailed(self, rc): - return False - - def _fsckErrorMessage(self, rc): - return _("Unknown return code: %d.") % (rc,) - - def doCheck(self): - if not self.exists: - raise FSError("filesystem has not been created") - - if not self.fsckProg: - return - - if not os.path.exists(self.device): - raise FSError("device does not exist") - - try: - ret = util.run_program([self.fsckProg] + self._getCheckArgs()) - except OSError as e: - raise FSError("filesystem check failed: %s" % e) - - if self._fsckFailed(ret): - hdr = _("%(type)s filesystem check failure on %(device)s: ") % \ - {"type": self.type, "device": self.device} - - msg = self._fsckErrorMessage(ret) - raise FSError(hdr + msg) - - def loadModule(self): - """Load whatever kernel module is required to support this filesystem.""" - global kernel_filesystems - - if not self._modules or self.mountType in kernel_filesystems: - return - - for module in self._modules: - try: - rc = util.run_program(["modprobe", module]) - except OSError as e: - log.error("Could not load kernel module %s: %s" % (module, e)) - self._supported = False - return - - if rc: - log.error("Could not load kernel module %s" % module) - self._supported = False - return - - # If we successfully loaded a kernel module, for this filesystem, we - # also need to update the list of supported filesystems. - kernel_filesystems = get_kernel_filesystems() - - def testMount(self, options=None): - """ Try to mount the fs and return True if successful. """ - ret = False - - if self.status: - raise RuntimeError("filesystem is already mounted") - - # create a temp dir - prefix = "%s.%s" % (os.path.basename(self.device), self.type) - mountpoint = tempfile.mkdtemp(prefix=prefix) - - # try the mount - try: - self.mount(mountpoint=mountpoint) - except Exception as e: - log.info("test mount failed: %s" % e) - else: - self.unmount() - ret = True - finally: - os.rmdir(mountpoint) - - return ret - - def mount(self, *args, **kwargs): - """ Mount this filesystem. - - Arguments: - - None - - Keyword Arguments: - - options -- mount options (overrides all other option strings) - chroot -- prefix to apply to mountpoint - mountpoint -- mountpoint (overrides self.mountpoint) - """ - options = kwargs.get("options", "") - chroot = kwargs.get("chroot", "/") - mountpoint = kwargs.get("mountpoint") - - if not self.exists: - raise FSError("filesystem has not been created") - - if not mountpoint: - mountpoint = self.mountpoint - - if not mountpoint: - raise FSError("no mountpoint given") - - if self.status: - return - - if not isinstance(self, NoDevFS) and not os.path.exists(self.device): - raise FSError("device %s does not exist" % self.device) - - # XXX os.path.join is FUBAR: - # - # os.path.join("/mnt/foo", "/") -> "/" - # - #mountpoint = os.path.join(chroot, mountpoint) - chrootedMountpoint = os.path.normpath("%s/%s" % (chroot, mountpoint)) - util.makedirs(chrootedMountpoint) - if flags.selinux: - ret = util.reset_file_context(mountpoint, chroot) - log.info("set SELinux context for mountpoint %s to %s" \ - % (mountpoint, ret)) - - # passed in options override default options - if not options or not isinstance(options, str): - options = self.options - - if isinstance(self, BindFS): - options = "bind," + options - - try: - rc = util.mount(self.device, chrootedMountpoint, - fstype=self.mountType, - options=options) - except Exception as e: - raise FSError("mount failed: %s" % e) - - if rc: - raise FSError("mount failed: %s" % rc) - - if flags.selinux and "ro" not in options.split(","): - ret = util.reset_file_context(mountpoint, chroot) - log.info("set SELinux context for newly mounted filesystem " - "root at %s to %s" %(mountpoint, ret)) - util.set_file_context("%s/lost+found" % mountpoint, - lost_and_found_context, chroot) - - self._mountpoint = chrootedMountpoint - - def unmount(self): - """ Unmount this filesystem. """ - if not self.exists: - raise FSError("filesystem has not been created") - - if not self._mountpoint: - # not mounted - return - - if not os.path.exists(self._mountpoint): - raise FSError("mountpoint does not exist") - - rc = util.umount(self._mountpoint) - if rc: - raise FSError("umount failed") - - self._mountpoint = None - - def _getLabelArgs(self, label): - argv = [] - argv.extend(self.defaultLabelOptions) - argv.extend([self.device, label]) - return argv - - def writeLabel(self, label): - """ Create a label for this filesystem. """ - if not self.exists: - raise FSError("filesystem has not been created") - - if not self.labelfsProg: - return - - if not os.path.exists(self.device): - raise FSError("device does not exist") - - argv = self._getLabelArgs(label) - rc = util.run_program([self.labelfsProg] + argv) - if rc: - raise FSError("label failed") - - self.label = label - self.notifyKernel() - - def _getRandomUUID(self): - uuid = util.capture_output(["uuidgen"]).strip() - return uuid - - def writeRandomUUID(self): - raise NotImplementedError("FS does not implement writeRandomUUID") - - @property - def needsFSCheck(self): - return False - - @property - def mkfsProg(self): - """ Program used to create filesystems of this type. """ - return self._mkfs - - @property - def fsckProg(self): - """ Program used to check filesystems of this type. """ - return self._fsck - - @property - def resizefsProg(self): - """ Program used to resize filesystems of this type. """ - return self._resizefs - - @property - def labelfsProg(self): - """ Program used to manage labels for this filesystem type. """ - return self._labelfs - - @property - def infofsProg(self): - """ Program used to get information for this filesystem type. """ - return self._infofs - - @property - def utilsAvailable(self): - # we aren't checking for fsck because we shouldn't need it - for prog in [self.mkfsProg, self.resizefsProg, self.labelfsProg, - self.infofsProg]: - if not prog: - continue - - if not util.find_program_in_path(prog): - return False - - return True - - @property - def supported(self): - log_method_call(self, supported=self._supported) - return self._supported and self.utilsAvailable - - @property - def mountable(self): - canmount = (self.mountType in kernel_filesystems) or \ - (os.access("/sbin/mount.%s" % (self.mountType,), os.X_OK)) - modpath = os.path.realpath(os.path.join("/lib/modules", os.uname()[2])) - modname = "%s.ko" % self.mountType - - if not canmount and os.path.isdir(modpath): - for root, dirs, files in os.walk(modpath): - have = filter(lambda x: x.startswith(modname), files) - if len(have) == 1 and have[0].startswith(modname): - return True - - return canmount - - @property - def resizable(self): - """ Can formats of this filesystem type be resized? """ - return super(FS, self).resizable and self.utilsAvailable - - @property - def defaultFormatOptions(self): - """ Default options passed to mkfs for this filesystem type. """ - # return a copy to prevent modification - return self._defaultFormatOptions[:] - - @property - def defaultMountOptions(self): - """ Default options passed to mount for this filesystem type. """ - # return a copy to prevent modification - return self._defaultMountOptions[:] - - @property - def defaultLabelOptions(self): - """ Default options passed to labeler for this filesystem type. """ - # return a copy to prevent modification - return self._defaultLabelOptions[:] - - @property - def defaultCheckOptions(self): - """ Default options passed to checker for this filesystem type. """ - # return a copy to prevent modification - return self._defaultCheckOptions[:] - - def _getOptions(self): - options = ",".join(self.defaultMountOptions) - if self.mountopts: - # XXX should we clobber or append? - options = self.mountopts - return options - - def _setOptions(self, options): - self.mountopts = options - - options = property(_getOptions, _setOptions) - - @property - def mountType(self): - if not self._mountType: - self._mountType = self._type - - return self._mountType - - # These methods just wrap filesystem-specific methods in more - # generically named methods so filesystems and formatted devices - # like swap and LVM physical volumes can have a common API. - def create(self, *args, **kwargs): - if self.exists: - raise FSError("filesystem already exists") - - DeviceFormat.create(self, *args, **kwargs) - - return self.doFormat(*args, **kwargs) - - def setup(self, *args, **kwargs): - """ Mount the filesystem. - - The filesystem will be mounted at the directory indicated by - self.mountpoint. - """ - return self.mount(**kwargs) - - def teardown(self, *args, **kwargs): - return self.unmount(*args, **kwargs) - - @property - def status(self): - # FIXME check /proc/mounts or similar - if not self.exists: - return False - return self._mountpoint is not None - - def sync(self, root="/"): - pass - -class Ext2FS(FS): - """ ext2 filesystem. """ - _type = "ext2" - _mkfs = "mke2fs" - _modules = ["ext2"] - _resizefs = "resize2fs" - _labelfs = "e2label" - _fsck = "e2fsck" - _fsckErrors = {4: _("File system errors left uncorrected."), - 8: _("Operational error."), - 16: _("Usage or syntax error."), - 32: _("e2fsck cancelled by user request."), - 128: _("Shared library error.")} - _packages = ["e2fsprogs"] - _formattable = True - _supported = True - _resizable = True - _linuxNative = True - _maxSize = 8 * 1024 * 1024 - _minSize = 0 - _defaultFormatOptions = [] - _defaultMountOptions = ["defaults"] - _defaultCheckOptions = ["-f", "-p", "-C", "0"] - _dump = True - _check = True - _infofs = "dumpe2fs" - _defaultInfoOptions = ["-h"] - _existingSizeFields = ["Block count:", "Block size:"] - _fsProfileSpecifier = "-T" - partedSystem = fileSystemType["ext2"] - - def __init__(self, *args, **kwargs): - self.dirty = False - self.errors = False - super(Ext2FS, self).__init__(*args, **kwargs) - - def _fsckFailed(self, rc): - for errorCode in self._fsckErrors.keys(): - if rc & errorCode: - return True - return False - - def _fsckErrorMessage(self, rc): - msg = '' - - for errorCode in self._fsckErrors.keys(): - if rc & errorCode: - msg += "\n" + self._fsckErrors[errorCode] - - return msg.strip() - - def writeRandomUUID(self): - if not self.exists: - raise FSError("filesystem does not exist") - - err = None - try: - rc = util.run_program(["tune2fs", "-U", "random", self.device]) - except OSError as e: - err = str(e) - else: - if rc: - err = rc - - if err: - raise FSError("failed to set UUID for %s: %s" % (self.device, err)) - - @property - def minSize(self): - """ Minimum size for this filesystem in MB. """ - if self._minInstanceSize is None: - # try once in the beginning to get the minimum size for an - # existing filesystem. - size = self._minSize - blockSize = None - - if self.exists and os.path.exists(self.device): - # get block size - buf = util.capture_output([self.infofsProg, "-h", self.device]) - for line in buf.splitlines(): - if line.startswith("Block size:"): - blockSize = int(line.split(" ")[-1]) - - if line.startswith("Filesystem state:"): - self.dirty = "not clean" in line - self.errors = "with errors" in line - - if blockSize is None: - raise FSError("failed to get block size for %s filesystem " - "on %s" % (self.mountType, self.device)) - - # get minimum size according to resize2fs - buf = util.capture_output([self.resizefsProg, - "-P", self.device]) - for line in buf.splitlines(): - if "minimum size of the filesystem:" not in line: - continue - - # line will look like: - # Estimated minimum size of the filesystem: 1148649 - # - # NOTE: The minimum size reported is in blocks. Convert - # to bytes, then megabytes, and finally round up. - (text, sep, minSize) = line.partition(": ") - size = long(minSize) * blockSize - size = math.ceil(size / 1024.0 / 1024.0) - break - - if size is None: - log.warning("failed to get minimum size for %s filesystem " - "on %s" % (self.mountType, self.device)) - else: - orig_size = size - size = min(size * 1.1, size + 500, self.currentSize) - if orig_size < size: - log.debug("padding min size from %d up to %d" % (orig_size, size)) - else: - log.debug("using current size %d as min size" % size) - - self._minInstanceSize = size - - return self._minInstanceSize - - @property - def needsFSCheck(self): - return self.dirty or self.errors - - @property - def resizeArgs(self): - argv = ["-p", self.device, "%dM" % (self.targetSize,)] - return argv - -register_device_format(Ext2FS) - - -class Ext3FS(Ext2FS): - """ ext3 filesystem. """ - _type = "ext3" - _defaultFormatOptions = ["-t", "ext3"] - _modules = ["ext3"] - partedSystem = fileSystemType["ext3"] - - # It is possible for a user to specify an fsprofile that defines a blocksize - # smaller than the default of 4096 bytes and therefore to make liars of us - # with regard to this maximum filesystem size, but if they're doing such - # things they should know the implications of their chosen block size. - _maxSize = 16 * 1024 * 1024 - - @property - def needsFSCheck(self): - return self.errors - -register_device_format(Ext3FS) - - -class Ext4FS(Ext3FS): - """ ext4 filesystem. """ - _type = "ext4" - _defaultFormatOptions = ["-t", "ext4"] - _modules = ["ext4"] - partedSystem = fileSystemType["ext4"] - -register_device_format(Ext4FS) - - -class FATFS(FS): - """ FAT filesystem. """ - _type = "vfat" - _mkfs = "mkdosfs" - _modules = ["vfat"] - _labelfs = "dosfslabel" - _fsck = "dosfsck" - _fsckErrors = {1: _("Recoverable errors have been detected or dosfsck has " - "discovered an internal inconsistency."), - 2: _("Usage error.")} - _supported = True - _formattable = True - _maxSize = 1024 * 1024 - _packages = [ "dosfstools" ] - _defaultMountOptions = ["umask=0077", "shortname=winnt"] - # FIXME this should be fat32 in some cases - partedSystem = fileSystemType["fat16"] - - def _fsckFailed(self, rc): - if rc >= 1: - return True - return False - - def _fsckErrorMessage(self, rc): - return self._fsckErrors[rc] - -register_device_format(FATFS) - - -class EFIFS(FATFS): - _type = "efi" - _mountType = "vfat" - _modules = ["vfat"] - _name = "EFI System Partition" - _minSize = 50 - - @property - def supported(self): - return (isinstance(platform.platform, platform.EFI) and - self.utilsAvailable) - -register_device_format(EFIFS) - - -class BTRFS(FS): - """ btrfs filesystem """ - _type = "btrfs" - _mkfs = "mkfs.btrfs" - _modules = ["btrfs"] - _resizefs = "btrfsctl" - _formattable = True - _linuxNative = True - _maxLabelChars = 256 - _supported = True - _dump = True - _check = True - _packages = ["btrfs-progs"] - _minSize = 256 - _maxSize = 16 * 1024 * 1024 - # FIXME parted needs to be taught about btrfs so that we can set the - # partition table type correctly for btrfs partitions - # partedSystem = fileSystemType["btrfs"] - - def __init__(self, *args, **kwargs): - super(BTRFS, self).__init__(*args, **kwargs) - self.volUUID = kwargs.pop("volUUID", None) - - def create(self, *args, **kwargs): - # filesystem creation is done in storage.devicelibs.btrfs.create_volume - pass - - def destroy(self, *args, **kwargs): - # filesystem creation is done in storage.devicelibs.btrfs.delete_volume - pass - - def setup(self, *args, **kwargs): - log_method_call(self, type=self.mountType, device=self.device, - mountpoint=self.mountpoint) - if not self.mountpoint and "mountpoint" not in kwargs: - # Since btrfs vols have subvols the format setup is automatic. - # Don't try to mount it if there's no mountpoint. - return - - return self.mount(*args, **kwargs) - - def _getFormatOptions(self, options=None): - argv = [] - if options and isinstance(options, list): - argv.extend(options) - argv.extend(self.defaultFormatOptions) - if self.label: - argv.extend(["-L", self.label]) - argv.append(self.device) - return argv - - @property - def resizeArgs(self): - argv = ["-r", "%dm" % (self.targetSize,), self.device] - return argv - -register_device_format(BTRFS) - - -class GFS2(FS): - """ gfs2 filesystem. """ - _type = "gfs2" - _mkfs = "mkfs.gfs2" - _modules = ["dlm", "gfs2"] - _formattable = True - _defaultFormatOptions = ["-j", "1", "-p", "lock_nolock", "-O"] - _linuxNative = True - _supported = False - _dump = True - _check = True - _packages = ["gfs2-utils"] - # FIXME parted needs to be thaught about btrfs so that we can set the - # partition table type correctly for btrfs partitions - # partedSystem = fileSystemType["gfs2"] - - @property - def supported(self): - """ Is this filesystem a supported type? """ - supported = self._supported - if flags.gfs2: - supported = self.utilsAvailable - - return supported - -register_device_format(GFS2) - - -class JFS(FS): - """ JFS filesystem """ - _type = "jfs" - _mkfs = "mkfs.jfs" - _modules = ["jfs"] - _labelfs = "jfs_tune" - _defaultFormatOptions = ["-q"] - _defaultLabelOptions = ["-L"] - _maxLabelChars = 16 - _maxSize = 8 * 1024 * 1024 - _formattable = True - _linuxNative = True - _supported = False - _dump = True - _check = True - _infofs = "jfs_tune" - _defaultInfoOptions = ["-l"] - _existingSizeFields = ["Aggregate block size:", "Aggregate size:"] - partedSystem = fileSystemType["jfs"] - - @property - def supported(self): - """ Is this filesystem a supported type? """ - supported = self._supported - if flags.jfs: - supported = self.utilsAvailable - - return supported - -register_device_format(JFS) - - -class ReiserFS(FS): - """ reiserfs filesystem """ - _type = "reiserfs" - _mkfs = "mkreiserfs" - _resizefs = "resize_reiserfs" - _labelfs = "reiserfstune" - _modules = ["reiserfs"] - _defaultFormatOptions = ["-f", "-f"] - _defaultLabelOptions = ["-l"] - _maxLabelChars = 16 - _maxSize = 16 * 1024 * 1024 - _formattable = True - _linuxNative = True - _supported = False - _dump = True - _check = True - _packages = ["reiserfs-utils"] - _infofs = "debugreiserfs" - _defaultInfoOptions = [] - _existingSizeFields = ["Count of blocks on the device:", "Blocksize:"] - partedSystem = fileSystemType["reiserfs"] - - @property - def supported(self): - """ Is this filesystem a supported type? """ - supported = self._supported - if flags.reiserfs: - supported = self.utilsAvailable - - return supported - - @property - def resizeArgs(self): - argv = ["-s", "%dM" % (self.targetSize,), self.device] - return argv - -register_device_format(ReiserFS) - - -class XFS(FS): - """ XFS filesystem """ - _type = "xfs" - _mkfs = "mkfs.xfs" - _modules = ["xfs"] - _labelfs = "xfs_admin" - _defaultFormatOptions = ["-f"] - _defaultLabelOptions = ["-L"] - _maxLabelChars = 16 - _maxSize = 16 * 1024 * 1024 - _formattable = True - _linuxNative = True - _supported = True - _dump = True - _check = True - _packages = ["xfsprogs"] - _infofs = "xfs_db" - _defaultInfoOptions = ["-c", "\"sb 0\"", "-c", "\"p dblocks\"", - "-c", "\"p blocksize\""] - _existingSizeFields = ["dblocks =", "blocksize ="] - partedSystem = fileSystemType["xfs"] - - def _getLabelArgs(self, label): - argv = [] - argv.extend(self.defaultLabelOptions) - argv.extend([label, self.device]) - return argv - - def sync(self, root='/'): - """ Ensure that data we've written is at least in the journal. - - This is a little odd because xfs_freeze will only be - available under the install root. - """ - if not self.status or not self._mountpoint.startswith(root): - return - - try: - util.run_program(["xfs_freeze", "-f", self.mountpoint], root=root) - except OSError as e: - log.error("failed to run xfs_freeze: %s" % e) - - try: - util.run_program(["xfs_freeze", "-u", self.mountpoint], root=root) - except OSError as e: - log.error("failed to run xfs_freeze: %s" % e) - -register_device_format(XFS) - - -class HFS(FS): - _type = "hfs" - _mkfs = "hformat" - _modules = ["hfs"] - _formattable = True - partedSystem = fileSystemType["hfs"] - -register_device_format(HFS) - - -class AppleBootstrapFS(HFS): - _type = "appleboot" - _mountType = "hfs" - _name = "Apple Bootstrap" - _minSize = 800.00 / 1024.00 - _maxSize = 1 - - @property - def supported(self): - return (isinstance(platform.platform, platform.NewWorldPPC) - and self.utilsAvailable) - -register_device_format(AppleBootstrapFS) - - -class HFSPlus(FS): - _type = "hfs+" - _modules = ["hfsplus"] - _udevTypes = ["hfsplus"] - _mkfs = "mkfs.hfsplus" - _fsck = "fsck.hfsplus" - _packages = ["hfsplus-tools"] - _formattable = True - _mountType = "hfsplus" - _minSize = 1 - _maxSize = 2 * 1024 * 1024 - _check = True - partedSystem = fileSystemType["hfs+"] - -register_device_format(HFSPlus) - - -class NTFS(FS): - """ ntfs filesystem. """ - _type = "ntfs" - _resizefs = "ntfsresize" - _fsck = "ntfsresize" - _resizable = True - _minSize = 1 - _maxSize = 16 * 1024 * 1024 - _defaultMountOptions = ["defaults", "ro"] - _defaultCheckOptions = ["-c"] - _packages = ["ntfsprogs"] - _infofs = "ntfsinfo" - _defaultInfoOptions = ["-m"] - _existingSizeFields = ["Cluster Size:", "Volume Size in Clusters:"] - partedSystem = fileSystemType["ntfs"] - - @property - def mountable(self): - return False - - def _fsckFailed(self, rc): - if rc != 0: - return True - return False - - @property - def minSize(self): - """ The minimum filesystem size in megabytes. """ - if self._minInstanceSize is None: - # we try one time to determine the minimum size. - size = self._minSize - if self.exists and os.path.exists(self.device) and \ - util.find_program_in_path(self.resizefsProg): - minSize = None - buf = util.run_program([self.resizefsProg, "-m", self.device]) - for l in buf.split("\n"): - if not l.startswith("Minsize"): - continue - try: - minSize = int(l.split(":")[1].strip()) # MB - minSize *= (mb / mib) # MiB - except (IndexError, ValueError) as e: - minSize = None - log.warning("Unable to parse output for minimum size on %s: %s" %(self.device, e)) - - if minSize is None: - log.warning("Unable to discover minimum size of filesystem " - "on %s" %(self.device,)) - else: - size = min(minSize * 1.1, minSize + 500, self.currentSize) - if minSize < size: - log.debug("padding min size from %d up to %d" % (minSize, size)) - else: - log.debug("using current size %d as min size" % size) - - self._minInstanceSize = size - - return self._minInstanceSize - - @property - def resizeArgs(self): - # You must supply at least two '-f' options to ntfsresize or - # the proceed question will be presented to you. - - # FIXME: This -1 is because our partition alignment calculations plus - # converting back and forth between MiB and MB means the filesystem is - # getting resized to be slightly larger than the partition holding it. - # This hack just makes the filesystem fit. - targetSize = (mib / mb) * (self.targetSize-1) # convert MiB to MB - argv = ["-ff", "-s", "%dM" % (targetSize,), self.device] - return argv - - -register_device_format(NTFS) - - -# if this isn't going to be mountable it might as well not be here -class NFS(FS): - """ NFS filesystem. """ - _type = "nfs" - _modules = ["nfs"] - - def _deviceCheck(self, devspec): - if devspec is not None and ":" not in devspec: - raise ValueError("device must be of the form <host>:<path>") - - @property - def mountable(self): - return False - - def _setDevice(self, devspec): - self._deviceCheck(devspec) - self._device = devspec - - def _getDevice(self): - return self._device - - device = property(lambda f: f._getDevice(), - lambda f,d: f._setDevice(d), - doc="Full path the device this format occupies") - -register_device_format(NFS) - - -class NFSv4(NFS): - """ NFSv4 filesystem. """ - _type = "nfs4" - _modules = ["nfs4"] - -register_device_format(NFSv4) - - -class Iso9660FS(FS): - """ ISO9660 filesystem. """ - _type = "iso9660" - _formattable = False - _supported = True - _resizable = False - _linuxNative = False - _dump = False - _check = False - _defaultMountOptions = ["ro"] - -register_device_format(Iso9660FS) - - -class NoDevFS(FS): - """ nodev filesystem base class """ - _type = "nodev" - - def __init__(self, *args, **kwargs): - FS.__init__(self, *args, **kwargs) - self.exists = True - self.device = self.type - - def _setDevice(self, devspec): - self._device = devspec - - def _getExistingSize(self): - pass - -register_device_format(NoDevFS) - - -class DevPtsFS(NoDevFS): - """ devpts filesystem. """ - _type = "devpts" - _defaultMountOptions = ["gid=5", "mode=620"] - -register_device_format(DevPtsFS) - - -# these don't really need to be here -class ProcFS(NoDevFS): - _type = "proc" - -register_device_format(ProcFS) - - -class SysFS(NoDevFS): - _type = "sysfs" - -register_device_format(SysFS) - - -class TmpFS(NoDevFS): - _type = "tmpfs" - -register_device_format(TmpFS) - - -class BindFS(FS): - _type = "bind" - - @property - def mountable(self): - return True - - def _getExistingSize(self): - pass - -register_device_format(BindFS) - - -class SELinuxFS(NoDevFS): - _type = "selinuxfs" - - @property - def mountable(self): - return flags.selinux and super(SELinuxFS, self).mountable - -register_device_format(SELinuxFS) - - -class USBFS(NoDevFS): - _type = "usbfs" - -register_device_format(USBFS) - |