Skip to content

Commit

Permalink
Add dynamic mountpoint detection support
Browse files Browse the repository at this point in the history
Current format._mountpoint attribute for "active" mountpoints is
being replaced with property format.systemMountpoint that returns
current mountpoint based on system information (cached information
from /proc/mounts and /proc/self/mountinfo)

Signed-off-by: Vojtech Trefny <vtrefny@redhat.com>
  • Loading branch information
vojtechtrefny committed Mar 18, 2015
1 parent 201e2a0 commit 6ca637e
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 19 deletions.
48 changes: 29 additions & 19 deletions blivet/formats/fs.py
Expand Up @@ -39,6 +39,7 @@
from ..size import B, KiB, MiB, GiB, KB, MB, GB
from ..i18n import _, N_
from .. import udev
from ..mounts import mountsCache

import logging
log = logging.getLogger("blivet")
Expand Down Expand Up @@ -110,7 +111,6 @@ def __init__(self, **kwargs):
# filesystem size does not necessarily equal device size
self._size = kwargs.get("size", Size(0))
self._minInstanceSize = Size(0) # min size of this FS instance
self._mountpoint = None # the current mountpoint when mounted

# Resize operations are limited to error-free filesystems whose current
# size is known.
Expand Down Expand Up @@ -582,6 +582,13 @@ def loadModule(self):
# also need to update the list of supported filesystems.
update_kernel_filesystems()

@property
def systemMountpoint(self):
if not self.exists:
return None

return mountsCache.getMountpoint(self.device)

def testMount(self):
""" Try to mount the fs and return True if successful. """
ret = False
Expand Down Expand Up @@ -665,29 +672,25 @@ def mount(self, options="", chroot="/", mountpoint=None):
if not ret:
log.warning("Failed to set SELinux context for newly mounted filesystem lost+found directory at %s to %s", lost_and_found_path, lost_and_found_context)

self._mountpoint = chrootedMountpoint

def unmount(self):
""" Unmount this filesystem. """
if not self.exists:
raise FSError("filesystem has not been created")

if not self._mountpoint:
if not self.systemMountpoint:
# not mounted
return

if not os.path.exists(self._mountpoint):
if not os.path.exists(self.systemMountpoint):
raise FSError("mountpoint does not exist")

udev.settle()
rc = util.umount(self._mountpoint)
rc = util.umount(self.systemMountpoint)
if rc:
# try and catch whatever is causing the umount problem
util.run_program(["lsof", self._mountpoint])
util.run_program(["lsof", self.systemMountpoint])
raise FSError("umount failed")

self._mountpoint = None

def readLabel(self):
"""Read this filesystem's label.
Expand Down Expand Up @@ -900,7 +903,7 @@ def status(self):
# FIXME check /proc/mounts or similar
if not self.exists:
return False
return self._mountpoint is not None
return self.systemMountpoint is not None

def sync(self, root="/"):
pass
Expand Down Expand Up @@ -1160,6 +1163,13 @@ def setup(self, **kwargs):

return self.mount(**kwargs)

@property
def systemMountpoint(self):
if not self.exists:
return None

return mountsCache.getMountpoint(self.device, self.subvolspec)

register_device_format(BTRFS)


Expand Down Expand Up @@ -1272,16 +1282,16 @@ def sync(self, root='/'):
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):
if not self.status or not self.systemMountpoint.startswith(root):
return

try:
util.run_program(["xfs_freeze", "-f", self.mountpoint], root=root)
util.run_program(["xfs_freeze", "-f", self.systemMountpoint], 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)
util.run_program(["xfs_freeze", "-u", self.systemMountpoint], root=root)
except OSError as e:
log.error("failed to run xfs_freeze: %s", e)

Expand Down Expand Up @@ -1605,14 +1615,14 @@ def _setOptions(self, options):

@property
def free(self):
if self._mountpoint:
# If self._mountpoint is defined, it means this tmpfs mount
if self.systeMountpoint:
# If self.systeMountpoint is defined, it means this tmpfs mount
# has been mounted and there is a path we can use as a handle to
# look-up the free space on the filesystem.
# When running with changeroot, such as during installation,
# self._mountpoint is set to the full changeroot path once mounted,
# so even with changeroot, statvfs should still work fine.
st = os.statvfs(self._mountpoint)
# self.systeMountpoint is set to the full changeroot path once
# mounted so even with changeroot, statvfs should still work fine.
st = os.statvfs(self.systeMountpoint)
free_space = Size(st.f_bavail*st.f_frsize)
else:
# Free might be called even if the tmpfs mount has not been
Expand Down Expand Up @@ -1646,7 +1656,7 @@ def resizeArgs(self):
# if any mount options are defined, append them
if self._options:
remount_options = "%s,%s" % (remount_options, self._options)
return ['-o', remount_options, self._type, self._mountpoint]
return ['-o', remount_options, self._type, self.systeMountpoint]

def doResize(self):
# we need to override doResize, because the
Expand Down
87 changes: 87 additions & 0 deletions blivet/mounts.py
@@ -0,0 +1,87 @@
# mounts.py
# Active mountpoints cache.
#
# Copyright (C) 2015 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): Vojtech Trefny <vtrefny@redhat.com>
#

from . import util

import logging
log = logging.getLogger("blivet")

class MountsCache(object):

def __init__(self):
self.mountsHash = 0
self.mountpoints = {}

def getMountpoint(self, devspec, subvolspec=None):
""" Get mountpoint for selected device
:param devscpec: device specification, eg. "/dev/vda1"
:type devspec: str
:param subvolspec: btrfs subvolume specification, eg. ID or name
:type subvolspec: str
:returns: mountpoint (path)
:rtype: str or NoneType
"""

self._cacheCheck()

return self.mountpoints.get((devspec, subvolspec))

def _getActiveMounts(self):
""" Get information about mounted devices from /proc/mounts and
/proc/self/mountinfo
"""

for line in open("/proc/mounts").readlines():
try:
(devspec, mountpoint, fstype, options, _rest) = line.split(None, 4)
except ValueError:
log.error("failed to parse /proc/mounts line: %s", line)
continue

if fstype == "btrfs":
# get the subvol name from /proc/self/mountinfo
for line in open("/proc/self/mountinfo").readlines():
fields = line.split()
_subvol = fields[3]
_mountpoint = fields[4]
_devspec = fields[9]
if _mountpoint == mountpoint and _devspec == devspec:
# empty _subvol[1:] means it is a top-level volume
subvolspec = _subvol[1:] or 5

self.mountpoints[(devspec, subvolspec)] = mountpoint

else:
self.mountpoints[(devspec, None)] = mountpoint

def _cacheCheck(self):

md5hash = util.md5_file("/proc/mounts")

if md5hash != self.mountsHash:
self.mountsHash = md5hash
self.mountpoints = {}
self._getActiveMounts()

mountsCache = MountsCache()

0 comments on commit 6ca637e

Please sign in to comment.