[CRIU] [RFC PATCH 01/21] zdtm: zdtm.py: elaborate file copying logic for ns tests

Ivan Shapovalov intelfx at intelfx.name
Fri Feb 19 06:50:24 PST 2016


From: Ivan Shapovalov <intelfx100 at gmail.com>

When copying files (tests' deps and supplementary binaries) to the "chroot",
do not blindly copy them using provided pathes (like "/sbin/iptables").
Instead, walk the path manually and replicate all encountered symlinks in
the "chroot".

This helps to support setups like Arch, where /{,s}bin are links to /usr/bin,
and only /usr/bin is in $PATH.

Signed-off-by: Ivan Shapovalov <intelfx at intelfx.name>
---
 test/zdtm.py | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 61 insertions(+), 11 deletions(-)

diff --git a/test/zdtm.py b/test/zdtm.py
index 01b20e7..79b7a95 100755
--- a/test/zdtm.py
+++ b/test/zdtm.py
@@ -105,8 +105,15 @@ class host_flavor:
 	def clean():
 		pass
 
+def splitall(path, tail = []):
+	(dir, name) = os.path.split(path)
+	if dir and dir != path:
+		return splitall(dir, [name] + tail)
+	else:
+		return [dir or name] + tail
+
 class ns_flavor:
-	__root_dirs = ["/bin", "/sbin", "/etc", "/lib", "/lib64", "/dev", "/dev/pts", "/dev/net", "/tmp", "/usr", "/proc"]
+	__root_dirs = ["/etc", "/dev", "/dev/pts", "/dev/net", "/tmp", "/proc"]
 
 	def __init__(self, opts):
 		self.name = "ns"
@@ -115,18 +122,64 @@ class ns_flavor:
 		self.root = make_tests_root()
 		self.root_mounted = False
 
-	def __copy_one(self, fname):
-		tfname = self.root + fname
-		if not os.access(tfname, os.F_OK):
+	def __copy_one(self, src):
+		#
+		# let's copy the whole subhierarchy, accounting neatly for cases like /sbin/iptables on Arch
+		# (where /sbin -> /usr/bin, /usr/bin/iptables -> /usr/bin/xtables-multi, and only /usr/bin is in $PATH).`
+		#
+
+		src_bits = splitall(src)
+		if src_bits[0] != "/":
+			raise test_fail_exc("Deps check (%s is not absolute)" % src)
+		del src_bits[0]
+
+		src_dir_so_far = "/"
+
+		# copy all directories and links accurately
+		for bit_src in src_bits:
+			src_so_far = os.path.join(src_dir_so_far, bit_src)
+			dest_so_far = self.root + src_so_far
+
+			# handle chained symlinks
+			while os.path.islink(src_so_far):
+				bit_dst = os.readlink(src_so_far)
+				# relativize symlink target
+				bit_dst_rel = os.path.relpath(bit_dst, src_dir_so_far) if os.path.isabs(bit_dst) else bit_dst
+
+				try:
+					os.symlink(bit_dst_rel, dest_so_far)
+				except:
+					pass
+
+				src_so_far = os.path.join(src_dir_so_far, bit_dst)
+				dest_so_far = self.root + src_so_far
+				src_dir_so_far = os.path.dirname(src_so_far)
+				try:
+					os.makedirs(os.path.dirname(dest_so_far))
+				except:
+					pass
+
+			if os.path.isdir(src_so_far):
+				src_dir_so_far = src_so_far
+				try:
+					os.makedirs(dest_so_far)
+				except:
+					pass
+			else:
+				break
+
+		# finally, copy the file itself
+		real_dest = self.root + os.path.realpath(src)
+		if not os.access(real_dest, os.F_OK):
 			# Copying should be atomic as tests can be
 			# run in parallel
 			try:
-				os.makedirs(self.root + os.path.dirname(fname))
+				os.makedirs(os.path.dirname(real_dest))
 			except:
 				pass
-			dst = tempfile.mktemp(".tso", "", self.root + os.path.dirname(fname))
-			shutil.copy2(fname, dst)
-			os.rename(dst, tfname)
+			temp = tempfile.mktemp(".tso", "", os.path.dirname(real_dest))
+			shutil.copy2(src, temp)
+			os.rename(temp, real_dest)
 
 	def __copy_libs(self, binary):
 		ldd = subprocess.Popen(["ldd", binary], stdout = subprocess.PIPE)
@@ -164,9 +217,6 @@ class ns_flavor:
 			os.mkdir(self.root + dir)
 			os.chmod(self.root + dir, 0777)
 
-		for ldir in [ "/bin", "/sbin", "/lib", "/lib64" ]:
-			os.symlink(".." + ldir, self.root + "/usr" + ldir)
-
 		self.__mknod("tty", os.makedev(5, 0))
 		self.__mknod("null", os.makedev(1, 3))
 		self.__mknod("net/tun")
-- 
2.7.1



More information about the CRIU mailing list