>From e3326b19721ffdc75414fad32174dea203377f67 Mon Sep 17 00:00:00 2001
From: Colin Walters <walters@verbum.org>
Date: Wed, 19 Mar 2014 10:56:24 -0400
Subject: [PATCH] Split ROOT_PATH usage into
 getTargetPhysicalRoot()/getSysroot()

This mirrors the change to Anaconda, in order to enable OSTree
support.

For OSTree, the location of the OS checkout (and e.g. /etc/fstab) is
really in /ostree/deploy/$osname/deploy/$revision/etc/fstab.

In order to properly support OSTree, Blivet will need to gain an
understanding of the separation between the physical system / and the
target root.

This patch will be used in Anaconda, which will call setSysroot()
attribute after the root being installed is laid out.

After that, when we call write(), the fstab data will be correctly
written into the target root.
---
 blivet/__init__.py        | 106 ++++++++++++++++++++++++++++++++--------------
 blivet/devicelibs/dasd.py |   4 +-
 blivet/fcoe.py            |   8 ++--
 blivet/iscsi.py           |  14 +++---
 blivet/util.py            |   4 +-
 blivet/zfcp.py            |   6 +--
 6 files changed, 93 insertions(+), 49 deletions(-)

diff --git a/blivet/__init__.py b/blivet/__init__.py
index e8e757c..987e29f 100644
--- a/blivet/__init__.py
+++ b/blivet/__init__.py
@@ -29,7 +29,10 @@ __version__ = '0.50'
 ## enable_installer_mode is called.
 ##
 isys = None
+iutil = None
 ROOT_PATH = '/'
+_storageRoot = ROOT_PATH
+_sysroot = ROOT_PATH
 shortProductName = 'blivet'
 productName = 'blivet'
 ERROR_RAISE = 0
@@ -99,7 +102,10 @@ log = logging.getLogger("blivet")
 def enable_installer_mode():
     """ Configure the module for use by anaconda (OS installer). """
     global isys
+    global iutil
     global ROOT_PATH
+    global _storageRoot
+    global _sysroot
     global shortProductName
     global productName
     global get_bootloader
@@ -107,6 +113,7 @@ def enable_installer_mode():
     global ERROR_RAISE
 
     from pyanaconda import isys # pylint: disable=redefined-outer-name
+    from pyanaconda import iutil # pylint: disable=redefined-outer-name
     from pyanaconda.constants import ROOT_PATH # pylint: disable=redefined-outer-name
     from pyanaconda.constants import shortProductName # pylint: disable=redefined-outer-name
     from pyanaconda.constants import productName # pylint: disable=redefined-outer-name
@@ -114,6 +121,13 @@ def enable_installer_mode():
     from pyanaconda.errors import errorHandler # pylint: disable=redefined-outer-name
     from pyanaconda.errors import ERROR_RAISE # pylint: disable=redefined-outer-name
 
+    if hasattr(iutil, 'getTargetPhysicalRoot'):
+        # Introduced in newer Anaconda
+        _storageRoot = iutil.getTargetPhysicalRoot()
+        _sysroot = iutil.getSysroot()
+    else:
+        _storageRoot = _sysroot = ROOT_PATH
+
     from pyanaconda.anaconda_log import program_log_lock
     util.program_log_lock = program_log_lock
 
@@ -123,6 +137,36 @@ def enable_installer_mode():
     from pyanaconda.progress import progress_report
     deviceaction.progress_report = progress_report
 
+def getSysroot():
+    """Returns the path to the target OS installation.
+    
+    For traditional installations, this is the same as the physical
+    storage root.
+    """
+    return _sysroot
+
+def getTargetPhysicalRoot():
+    """Returns the path to the "physical" storage root.
+    
+    This may be distinct from the sysroot, which could be a
+    chroot-type subdirectory of the physical root.  This is used for
+    example by all OSTree-based installations.
+    """
+    return _storageRoot
+
+def setSysroot(storageRoot, sysroot=None):
+    """Change the OS root path.
+       :param storageRoot: The root of physical storage
+       :param sysroot: An optional chroot subdirectory of storageRoot
+
+    Change the
+    """
+    global _storageRoot
+    global _sysroot
+    _storageRoot = _sysroot = storageRoot
+    if sysroot is not None:
+        _sysroot = sysroot
+
 def storageInitialize(storage, ksdata, protected):
     """ Perform installer-specific storage initialization. """
     from pyanaconda.flags import flags as anaconda_flags
@@ -202,7 +246,7 @@ def writeEscrowPackets(storage):
     backupPassphrase = generateBackupPassphrase()
 
     try:
-        escrowDir = ROOT_PATH + "/root"
+        escrowDir = _sysroot + "/root"
         log.debug("escrow: writing escrow packets to %s", escrowDir)
         util.makedirs(escrowDir)
         for device in escrowDevices:
@@ -1707,22 +1751,22 @@ class Blivet(object):
 
     def write(self):
         """ Write out all storage-related configuration files. """
-        if not os.path.isdir("%s/etc" % ROOT_PATH):
-            os.mkdir("%s/etc" % ROOT_PATH)
+        if not os.path.isdir("%s/etc" % _sysroot):
+            os.mkdir("%s/etc" % _sysroot)
 
         self.fsset.write()
         self.makeMtab()
-        self.iscsi.write(ROOT_PATH, self)
-        self.fcoe.write(ROOT_PATH)
-        self.zfcp.write(ROOT_PATH)
-        write_dasd_conf(self.dasd, ROOT_PATH)
+        self.iscsi.write(_sysroot, self)
+        self.fcoe.write(_sysroot)
+        self.zfcp.write(_sysroot)
+        write_dasd_conf(self.dasd, _sysroot)
 
     def turnOnSwap(self, upgrading=None):
-        self.fsset.turnOnSwap(rootPath=ROOT_PATH,
+        self.fsset.turnOnSwap(rootPath=_sysroot,
                               upgrading=upgrading)
 
     def mountFilesystems(self, raiseErrors=None, readOnly=None, skipRoot=False):
-        self.fsset.mountFilesystems(rootPath=ROOT_PATH,
+        self.fsset.mountFilesystems(rootPath=_sysroot,
                                     raiseErrors=raiseErrors,
                                     readOnly=readOnly, skipRoot=skipRoot)
 
@@ -1839,7 +1883,7 @@ class Blivet(object):
     def makeMtab(self):
         path = "/etc/mtab"
         target = "/proc/self/mounts"
-        path = os.path.normpath("%s/%s" % (ROOT_PATH, path))
+        path = os.path.normpath("%s/%s" % (_sysroot, path))
 
         if os.path.islink(path):
             # return early if the mtab symlink is already how we like it
@@ -2143,7 +2187,7 @@ def mountExistingSystem(fsset, rootDevice,
                         allowDirty=None, dirtyCB=None,
                         readOnly=None):
     """ Mount filesystems specified in rootDevice's /etc/fstab file. """
-    rootPath = ROOT_PATH
+    rootPath = _sysroot
     if dirtyCB is None:
         dirtyCB = lambda l: False
 
@@ -2185,7 +2229,7 @@ def mountExistingSystem(fsset, rootDevice,
     if dirtyDevs and (not allowDirty or dirtyCB(dirtyDevs)):
         raise DirtyFSError(dirtyDevs)
 
-    fsset.mountFilesystems(rootPath=ROOT_PATH, readOnly=readOnly, skipRoot=True)
+    fsset.mountFilesystems(rootPath=_sysroot, readOnly=readOnly, skipRoot=True)
 
 
 class BlkidTab(object):
@@ -2556,7 +2600,7 @@ class FSSet(object):
                 loop mounts?
         """
         if not chroot or not os.path.isdir(chroot):
-            chroot = ROOT_PATH
+            chroot = _sysroot
 
         path = "%s/etc/fstab" % chroot
         if not os.access(path, os.R_OK):
@@ -2724,10 +2768,10 @@ class FSSet(object):
         self.active = False
 
     def createSwapFile(self, device, size):
-        """ Create and activate a swap file under ROOT_PATH. """
+        """ Create and activate a swap file under storage root. """
         filename = "/SWAP"
         count = 0
-        basedir = os.path.normpath("%s/%s" % (ROOT_PATH,
+        basedir = os.path.normpath("%s/%s" % (getTargetPhysicalRoot(),
                                               device.format.mountpoint))
         while os.path.exists("%s/%s" % (basedir, filename)) or \
               self.devicetree.getDeviceByName(filename):
@@ -2747,10 +2791,10 @@ class FSSet(object):
 
     def mkDevRoot(self):
         root = self.rootDevice
-        dev = "%s/%s" % (ROOT_PATH, root.path)
-        if not os.path.exists("%s/dev/root" %(ROOT_PATH,)) and os.path.exists(dev):
+        dev = "%s/%s" % (_sysroot, root.path)
+        if not os.path.exists("%s/dev/root" %(_sysroot,)) and os.path.exists(dev):
             rdev = os.stat(dev).st_rdev
-            os.mknod("%s/dev/root" % (ROOT_PATH,), stat.S_IFBLK | 0600, rdev)
+            os.mknod("%s/dev/root" % (_sysroot,), stat.S_IFBLK | 0600, rdev)
 
     @property
     def swapDevices(self):
@@ -2762,7 +2806,7 @@ class FSSet(object):
 
     @property
     def rootDevice(self):
-        for path in ["/", ROOT_PATH]:
+        for path in ["/", getTargetPhysicalRoot()]:
             for device in self.devices:
                 try:
                     mountpoint = device.format.mountpoint
@@ -2775,19 +2819,19 @@ class FSSet(object):
     def write(self):
         """ write out all config files based on the set of filesystems """
         # /etc/fstab
-        fstab_path = os.path.normpath("%s/etc/fstab" % ROOT_PATH)
+        fstab_path = os.path.normpath("%s/etc/fstab" % _sysroot)
         fstab = self.fstab()
         open(fstab_path, "w").write(fstab)
 
         # /etc/crypttab
-        crypttab_path = os.path.normpath("%s/etc/crypttab" % ROOT_PATH)
+        crypttab_path = os.path.normpath("%s/etc/crypttab" % _sysroot)
         crypttab = self.crypttab()
         origmask = os.umask(0077)
         open(crypttab_path, "w").write(crypttab)
         os.umask(origmask)
 
         # /etc/mdadm.conf
-        mdadm_path = os.path.normpath("%s/etc/mdadm.conf" % ROOT_PATH)
+        mdadm_path = os.path.normpath("%s/etc/mdadm.conf" % _sysroot)
         mdadm_conf = self.mdadmConf()
         if mdadm_conf:
             open(mdadm_path, "w").write(mdadm_conf)
@@ -3027,7 +3071,7 @@ def getReleaseString():
     """
     Attempt to identify the installation of a Linux distribution by checking
     a previously mounted filesystem for several files.  The filesystem must
-    be mounted under ROOT_PATH.
+    be mounted under the target physical root.
 
     :returns: The machine's arch, distribution name, and distribution version
     or None for any parts that cannot be determined
@@ -3037,23 +3081,23 @@ def getReleaseString():
     relVer = None
 
     try:
-        relArch = util.capture_output(["arch"], root=ROOT_PATH).strip()
+        relArch = util.capture_output(["arch"], root=_sysroot).strip()
     except OSError:
         relArch = None
 
-    filename = "%s/etc/redhat-release" % ROOT_PATH
+    filename = "%s/etc/redhat-release" % getSysroot()
     if os.access(filename, os.R_OK):
         (relName, relVer) = releaseFromRedhatRelease(filename)
     else:
-        filename = "%s/etc/os-release" % ROOT_PATH
+        filename = "%s/etc/os-release" % getSysroot()
         if os.access(filename, os.R_OK):
             (relName, relVer) = releaseFromOsRelease(filename)
 
     return (relArch, relName, relVer)
 
 def findExistingInstallations(devicetree):
-    if not os.path.exists(ROOT_PATH):
-        util.makedirs(ROOT_PATH)
+    if not os.path.exists(getTargetPhysicalRoot()):
+        util.makedirs(getTargetPhysicalRoot())
 
     roots = []
     for device in devicetree.leaves:
@@ -3069,13 +3113,13 @@ def findExistingInstallations(devicetree):
 
         options = device.format.options + ",ro"
         try:
-            device.format.mount(options=options, mountpoint=ROOT_PATH)
+            device.format.mount(options=options, mountpoint=getSysroot())
         except Exception: # pylint: disable=broad-except
             log_exception_info(log.warning, "mount of %s as %s failed", [device.name, device.format.type])
             device.teardown()
             continue
 
-        if not os.access(ROOT_PATH + "/etc/fstab", os.R_OK):
+        if not os.access(getSysroot() + "/etc/fstab", os.R_OK):
             device.teardown(recursive=True)
             continue
 
@@ -3136,7 +3180,7 @@ class Root(object):
 def parseFSTab(devicetree, chroot=None):
     """ parse /etc/fstab and return a tuple of a mount dict and swap list """
     if not chroot or not os.path.isdir(chroot):
-        chroot = ROOT_PATH
+        chroot = _sysroot 
 
     mounts = {}
     swaps = []
diff --git a/blivet/devicelibs/dasd.py b/blivet/devicelibs/dasd.py
index de4e8ec..2e28629 100644
--- a/blivet/devicelibs/dasd.py
+++ b/blivet/devicelibs/dasd.py
@@ -109,14 +109,14 @@ def dasd_needs_format(dasd):
     return False
 
 
-def write_dasd_conf(disks, ROOT_PATH):
+def write_dasd_conf(disks, root):
     """ Write /etc/dasd.conf to target system for all DASD devices
         configured during installation.
     """
     if not (arch.isS390() or disks):
         return
 
-    with open(os.path.realpath(ROOT_PATH + "/etc/dasd.conf"), "w") as f:
+    with open(os.path.realpath(root + "/etc/dasd.conf"), "w") as f:
         for dasd in sorted(disks, key=lambda d: d.name):
             fields = [dasd.busid] + dasd.getOpts()
             f.write("%s\n" % " ".join(fields),)
diff --git a/blivet/fcoe.py b/blivet/fcoe.py
index 84f0d73..5e8a3a6 100644
--- a/blivet/fcoe.py
+++ b/blivet/fcoe.py
@@ -141,15 +141,15 @@ class fcoe(object):
 
         return error_msg
 
-    def write(self, ROOT_PATH):
+    def write(self, root):
         if not self.nics:
             return
 
-        if not os.path.isdir(ROOT_PATH + "/etc/fcoe"):
-            os.makedirs(ROOT_PATH + "/etc/fcoe", 0755)
+        if not os.path.isdir(root + "/etc/fcoe"):
+            os.makedirs(root + "/etc/fcoe", 0755)
 
         for nic, dcb, auto_vlan in self.nics:
-            fd = os.open(ROOT_PATH + "/etc/fcoe/cfg-" + nic,
+            fd = os.open(root + "/etc/fcoe/cfg-" + nic,
                          os.O_RDWR | os.O_CREAT)
             os.write(fd, '# Created by anaconda\n')
             os.write(fd, '# Enable/Disable FCoE service at the Ethernet port\n')
diff --git a/blivet/iscsi.py b/blivet/iscsi.py
index 20e367b..0e44b35 100644
--- a/blivet/iscsi.py
+++ b/blivet/iscsi.py
@@ -357,7 +357,7 @@ class iscsi(object):
 
         self.stabilize()
 
-    def write(self, ROOT_PATH, storage):
+    def write(self, root, storage):
         if not self.initiatorSet:
             return
 
@@ -374,17 +374,17 @@ class iscsi(object):
             if autostart:
                 node.setParameter("node.startup", "automatic")
 
-        if not os.path.isdir(ROOT_PATH + "/etc/iscsi"):
-            os.makedirs(ROOT_PATH + "/etc/iscsi", 0755)
-        fd = os.open(ROOT_PATH + INITIATOR_FILE, os.O_RDWR | os.O_CREAT)
+        if not os.path.isdir(root + "/etc/iscsi"):
+            os.makedirs(root + "/etc/iscsi", 0755)
+        fd = os.open(root + INITIATOR_FILE, os.O_RDWR | os.O_CREAT)
         os.write(fd, "InitiatorName=%s\n" %(self.initiator))
         os.close(fd)
 
         # copy "db" files.  *sigh*
-        if os.path.isdir(ROOT_PATH + "/var/lib/iscsi"):
-            shutil.rmtree(ROOT_PATH + "/var/lib/iscsi")
+        if os.path.isdir(root + "/var/lib/iscsi"):
+            shutil.rmtree(root + "/var/lib/iscsi")
         if os.path.isdir("/var/lib/iscsi"):
-            shutil.copytree("/var/lib/iscsi", ROOT_PATH + "/var/lib/iscsi",
+            shutil.copytree("/var/lib/iscsi", root + "/var/lib/iscsi",
                             symlinks=True)
 
     def getNode(self, name, address, port, iface):
diff --git a/blivet/util.py b/blivet/util.py
index d90b9e9..f7b8c3d 100644
--- a/blivet/util.py
+++ b/blivet/util.py
@@ -279,13 +279,13 @@ def makedirs(path):
 
 def copy_to_system(source):
     # do the import now because enable_installer_mode() has finally been called.
-    from . import ROOT_PATH
+    from . import getSysroot
 
     if not os.access(source, os.R_OK):
         log.info("copy_to_system: source '%s' does not exist.", source)
         return False
 
-    target = ROOT_PATH + source
+    target = getSysroot() + source
     target_dir = os.path.dirname(target)
     log.debug("copy_to_system: '%s' -> '%s'.", source, target)
     if not os.path.isdir(target_dir):
diff --git a/blivet/zfcp.py b/blivet/zfcp.py
index 6405820..cf2996f 100644
--- a/blivet/zfcp.py
+++ b/blivet/zfcp.py
@@ -406,15 +406,15 @@ class ZFCP:
             except ValueError as e:
                 log.warn("%s", str(e))
 
-    def write(self, ROOT_PATH):
+    def write(self, root):
         if len(self.fcpdevs) == 0:
             return
-        f = open(ROOT_PATH + zfcpconf, "w")
+        f = open(root + zfcpconf, "w")
         for d in self.fcpdevs:
             f.write("%s\n" %(d,))
         f.close()
         
-        f = open(ROOT_PATH + "/etc/modprobe.conf", "a")
+        f = open(root + "/etc/modprobe.conf", "a")
         f.write("alias scsi_hostadapter zfcp\n")
         f.close()
 
-- 
1.8.3.1

