From f900c3f7f60905f0aecbdbb636d2925b7df3db37 Mon Sep 17 00:00:00 2001
From: David Lehman <dlehman@redhat.com>
Date: Fri, 8 Feb 2013 12:38:03 -0600
Subject: [PATCH] Add support for representing the system's nodev filesystem
 mounts.

The include_nodev flag must be set to True prior to calling
Blivet.getActiveMounts, which is called from within Blivet.reset (if
flags.installer_mode is False).

There can be multiple mounts of any given nodev filesystem type, which
presents a problem in terms of looking up devices by name. The chosen
solution is to add a unique identifier to each NoDevice instance's name.
---
 blivet/__init__.py   | 87 ++++++++++++++++++++++++++++++++--------------------
 blivet/devices.py    |  5 +--
 blivet/devicetree.py |  2 ++
 blivet/flags.py      |  4 +++
 blivet/formats/fs.py | 30 +++++++++++++-----
 5 files changed, 85 insertions(+), 43 deletions(-)

diff --git a/blivet/__init__.py b/blivet/__init__.py
index 3b0e23b..4b3edc1 100644
--- a/blivet/__init__.py
+++ b/blivet/__init__.py
@@ -54,6 +54,7 @@ from deviceaction import *
 from formats import getFormat
 from formats import get_device_format_class
 from formats import get_default_filesystem_type
+from formats.fs import nodev_filesystems
 from devicelibs.dm import name_from_dm_node
 from devicelibs.crypto import generateBackupPassphrase
 from devicelibs.mpath import MultipathConfigWriter
@@ -64,7 +65,7 @@ from devicelibs.lvm import get_pv_space
 from .partitioning import SameSizeSet
 from .partitioning import TotalSizeSet
 from .partitioning import doPartitioning
-from udev import *
+from udev import udev_trigger
 import iscsi
 import fcoe
 import zfcp
@@ -433,11 +434,7 @@ class Blivet(object):
             self.roots = findExistingInstallations(self.devicetree)
 
         if not flags.installer_mode:
-            # reflect current state of the system in fs mountpoint, status
-            mounts = parseMounts(self.devicetree)
-            for (mountpoint, device) in mounts.iteritems():
-                device.format.mountpoint = mountpoint
-                device.format._mountpoint = mountpoint
+            self.getActiveMounts()
 
         self.dumpState("initial")
 
@@ -2349,6 +2346,56 @@ class Blivet(object):
 
         return new
 
+    def getActiveMounts(self):
+        """ Reflect active mounts in the appropriate devices' formats. """
+        log.info("collecting information about active mounts")
+        for _line in open("/proc/mounts").readlines():
+            (line, pound, comment) = _line.partition("#")
+            try:
+                (devspec, mountpoint, fstype, options, rest) = line.split(None, 4)
+            except IndexError:
+                blivet_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:
+                        log.debug("subvol %s" % _subvol)
+                        options += ",subvol=%s" % _subvol[1:]
+
+            if fstype in nodev_filesystems:
+                if not flags.include_nodev:
+                    continue
+
+                log.info("found nodev %s filesystem mounted at %s"
+                            % (fstype, mountpoint))
+                # nodev filesystems require some special handling.
+                # For now, a lot of this is based on the idea that it's a losing
+                # battle to require the presence of an FS class for every type
+                # of nodev filesystem. Based on that idea, we just instantiate
+                # NoDevFS directly and then hack in the fstype as the device
+                # attribute.
+                fmt = getFormat("nodev")
+                fmt.device = fstype
+
+                # NoDevice also needs some special works since they don't have
+                # per-instance names in the kernel.
+                device = NoDevice(format=fmt)
+                n = len([d for d in self.devices if d.format.type == fstype])
+                device._name += ".%d" % n
+                self.devicetree._addDevice(device)
+                devspec = device.name
+
+            device = self.devicetree.resolveDevice(devspec, options=options)
+            if device is not None:
+                device.format.mountpoint = mountpoint   # for future mounts
+                device.format._mountpoint = mountpoint  # active mountpoint
+
 
 def mountExistingSystem(fsset, rootDevice,
                         allowDirty=None, dirtyCB=None,
@@ -3295,34 +3342,6 @@ def parseFSTab(devicetree, chroot=None):
 
     return (mounts, swaps)
 
-def parseMounts(devicetree):
-    """ Return a mountpoint:StorageDevice dict representing active mounts. """
-    mounts = {}
-    for _line in open("/proc/mounts").readlines():
-        (line, pound, comment) = _line.partition("#")
-        try:
-            (devspec, mountpoint, fstype, options, rest) = line.split(None, 4)
-        except IndexError:
-            blivet_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:
-                    log.debug("subvol %s" % _subvol)
-                    options += ",subvol=%s" % _subvol[1:]
-
-        device = devicetree.resolveDevice(devspec, options=options)
-        if device is not None:
-            mounts[mountpoint] = device
-
-    return mounts
-
 class DeviceFactory(object):
     type_desc = None
     member_format = None        # format type for member devices
diff --git a/blivet/devices.py b/blivet/devices.py
index 28a5a92..c7522b5 100644
--- a/blivet/devices.py
+++ b/blivet/devices.py
@@ -3415,7 +3415,7 @@ class NoDevice(StorageDevice):
                 format -- a DeviceFormat instance
         """
         if format:
-            name = format.type
+            name = format.device
         else:
             name = "none"
 
@@ -3424,7 +3424,8 @@ class NoDevice(StorageDevice):
     @property
     def path(self):
         """ Device node representing this device. """
-        return self.name
+        # the name may have a '.%d' suffix to make it unique
+        return self.name.split(".")[0]
 
     def setup(self, orig=False):
         """ Open, or set up, a device. """
diff --git a/blivet/devicetree.py b/blivet/devicetree.py
index 9e29e7b..57c94dd 100644
--- a/blivet/devicetree.py
+++ b/blivet/devicetree.py
@@ -2142,6 +2142,8 @@ class DeviceTree(object):
                 if edd_number == spec:
                     device = self.getDeviceByName(edd_name)
                     break
+        elif options and "nodev" in options.split(","):
+            device = self.getDeviceByName(devspec)
         else:
             if not devspec.startswith("/dev/"):
                 device = self.getDeviceByName(devspec)
diff --git a/blivet/flags.py b/blivet/flags.py
index ed0e5dc..e892b74 100644
--- a/blivet/flags.py
+++ b/blivet/flags.py
@@ -54,6 +54,10 @@ class Flags(object):
 
         self.gpt = False
 
+        # whether to include nodev filesystems in the devicetree (only
+        # meaningful when flags.installer_mode is False)
+        self.include_nodev = False
+
         self.boot_cmdline = {}
 
         self.update_from_boot_cmdline()
diff --git a/blivet/formats/fs.py b/blivet/formats/fs.py
index b472e87..225f54e 100644
--- a/blivet/formats/fs.py
+++ b/blivet/formats/fs.py
@@ -57,14 +57,19 @@ mib = 1024 * 1024.0
 
 fs_configs = {}
 
-def get_kernel_filesystems():
-    fs_list = []
+kernel_filesystems = []
+nodev_filesystems = []
+
+def update_kernel_filesystems():
+    global kernel_filesystems
+    global nodev_filesystems
     for line in open("/proc/filesystems").readlines():
-        fs_list.append(line.split()[-1])
-    return fs_list
+        fields = line.split()
+        kernel_filesystems.append(fields[-1])
+        if fields[0] == "nodev":
+            nodev_filesystems.append(fields[-1])
 
-global kernel_filesystems
-kernel_filesystems = get_kernel_filesystems()
+update_kernel_filesystems()
 
 def fsConfigFromFile(config_file):
     """ Generate a set of attribute name/value pairs with which a
@@ -485,7 +490,7 @@ class FS(DeviceFormat):
 
         # 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()
+        update_kernel_filesystems()
 
     def testMount(self, options=None):
         """ Try to mount the fs and return True if successful. """
@@ -1370,6 +1375,17 @@ class NoDevFS(FS):
     def _setDevice(self, devspec):
         self._device = devspec
 
+    @property
+    def type(self):
+        if self.device != self._type:
+            return self.device
+        else:
+            return self._type
+
+    @property
+    def mountType(self):
+        return self.device  # this is probably set to the real/specific fstype
+
     def _getExistingSize(self):
         pass
 
-- 
1.8.1

