Skip to content

Commit

Permalink
syscontainers: fix detection of repo location
Browse files Browse the repository at this point in the history
Improve the detection of the repo being on the same file system as the
checkout path by trying to link a file.  This is necessary since on a
bind mount the st_dev for the two directories is the same, but hardlinks
will still fail with EXDEV.

Also, change the test in _canonicalize_location to check that on an
Atomic Host /sysroot/ostree/deploy/*/var/* and /var/* have the same
st_dev and st_ino.  It is necessary so that files created under /sysroot
are visible in /var, and in this case it is fine the two locations are
on different file systems.

Signed-off-by: Giuseppe Scrivano <[email protected]>

Closes: projectatomic#1133
Approved by: ashcrow
  • Loading branch information
giuseppe authored and eyusupov committed Mar 10, 2018
1 parent baf145c commit 5e091e9
Showing 1 changed file with 55 additions and 7 deletions.
62 changes: 55 additions & 7 deletions Atomic/syscontainers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import stat # pylint: disable=bad-python3-import
import subprocess
import time
import errno
from .client import AtomicDocker
from Atomic.backends._docker_errors import NoDockerDaemon
from ctypes import cdll, CDLL
Expand Down Expand Up @@ -84,6 +85,7 @@ def __init__(self):
self.setvalues = None
self.display = False
self.runtime = None
self._repo_location = None
self._runtime_from_info_file = None

def get_atomic_config_item(self, config_item):
Expand Down Expand Up @@ -676,7 +678,45 @@ def _amend_values(self, values, manifest, name, image, image_id, destination, pr
return values

@staticmethod
def _are_on_the_same_filesystem(a, b):
def _is_repo_on_the_same_filesystem(repo, destdir):
"""
:param repo: Existing ostree repository.
:type repo: str
:param destdir: path to check. It is created if it doesn't exist. It must be
writeable as we create a temporary file there.
:type destdir: str
"""
repo_stat = os.stat(repo)
destdir_stat = os.stat(destdir)
# Simple case: st_dev is different
if repo_stat.st_dev != destdir_stat.st_dev:
return False

try:
os.makedirs(destdir)
except OSError as e:
if e.errno != errno.EEXIST:
raise

src_file = os.path.join(repo, "config")
dest_file = os.path.join(destdir, "samefs-check-{}".format(os.getpid()))
# Try to create a link, check if it fails with EXDEV
try:
os.link(src_file, dest_file)
except OSError as e:
if e.errno == errno.EXDEV:
return False
raise
finally:
try:
os.unlink(dest_file)
except OSError:
pass
return True

@staticmethod
def _are_same_file(a, b):
a_stat = os.stat(a)
b_stat = os.stat(b)
return a_stat.st_dev == b_stat.st_dev and a_stat.st_ino == b_stat.st_ino
Expand All @@ -698,8 +738,7 @@ def _canonicalize_location(self, destination):
# If it does, we use the ostree destination.
if not os.path.exists(os.path.dirname(destination)):
os.makedirs(os.path.dirname(destination))
if SystemContainers._are_on_the_same_filesystem(os.path.dirname(destination),
os.path.dirname(ostree_destination)):
if SystemContainers._are_same_file(os.path.dirname(destination), os.path.dirname(ostree_destination)):
destination = ostree_destination

except: #pylint: disable=bare-except
Expand Down Expand Up @@ -992,6 +1031,11 @@ def get_ostree_repo_location(self):
:returns: Path to the ostree repository.
:rtype: str
"""
if self._repo_location is None:
self._repo_location = self._find_ostree_repo_location()
return self._repo_location

def _find_ostree_repo_location(self):
location = os.environ.get("ATOMIC_OSTREE_REPO")
if location is not None:
return location
Expand All @@ -1008,11 +1052,15 @@ def get_ostree_repo_location(self):
checkouts = self._get_system_checkout_path()
if not os.path.exists(checkouts):
os.makedirs(checkouts)
rootdir = "/ostree/repo" if os.path.exists("/ostree/repo") else "/"
if not SystemContainers._are_on_the_same_filesystem(rootdir, checkouts):
return os.path.join(self.get_storage_path(skip_canonicalize=True), "ostree")

return "/ostree/repo"
storage_path = self.get_storage_path(skip_canonicalize=True)

# Use /ostree/repo if it already exists and it is on the same filesystem
# as the checkout directory.
if os.path.exists("/ostree/repo/config"):
if SystemContainers._is_repo_on_the_same_filesystem("/ostree/repo", storage_path):
return "/ostree/repo"
return os.path.join(storage_path, "ostree")

def _get_ostree_repo(self):
if not OSTREE_PRESENT:
Expand Down

0 comments on commit 5e091e9

Please sign in to comment.