aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArfrever Frehtes Taifersar Arahesis <Arfrever@Apache.Org>2019-08-07 19:06:11 +0200
committerZac Medico <zmedico@gentoo.org>2019-08-11 12:11:47 -0700
commit0b7eda500a0dcb98a67f33bf9ef25b202b358986 (patch)
tree373d366bd8146432a2719261af3a5152fb3b0f2a /lib/portage/util/_compare_files.py
parentlib/p/util/_dyn_libs/LinkageMapELF.py: get dep graph from EROOT. (diff)
downloadportage-0b7eda500a0dcb98a67f33bf9ef25b202b358986.tar.gz
portage-0b7eda500a0dcb98a67f33bf9ef25b202b358986.tar.bz2
portage-0b7eda500a0dcb98a67f33bf9ef25b202b358986.zip
dblink._collision_protect: Detect internal collisions.
Implement detection of internal collisions (between files of the same package, located in separate directories in the installation image (${D}) corresponding to merged directories in the target filesystem (${ROOT})). This provides protection against overwriting some files when performing merging of files from ${D} to ${ROOT} in some filesystem layouts (such as /-merged layout or /usr-merged layout). Internal collisions between identical files are silently ignored. Bug: https://bugs.gentoo.org/690484 Signed-off-by: Arfrever Frehtes Taifersar Arahesis <Arfrever@Apache.Org> Signed-off-by: Zac Medico <zmedico@gentoo.org>
Diffstat (limited to 'lib/portage/util/_compare_files.py')
-rw-r--r--lib/portage/util/_compare_files.py103
1 files changed, 103 insertions, 0 deletions
diff --git a/lib/portage/util/_compare_files.py b/lib/portage/util/_compare_files.py
new file mode 100644
index 000000000..bd993e501
--- /dev/null
+++ b/lib/portage/util/_compare_files.py
@@ -0,0 +1,103 @@
+# Copyright 2019 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+__all__ = ["compare_files"]
+
+import io
+import os
+import stat
+import sys
+
+from portage import _encodings
+from portage import _unicode_encode
+from portage.util._xattr import xattr
+
+def compare_files(file1, file2, skipped_types=()):
+ """
+ Compare metadata and contents of two files.
+
+ @param file1: File 1
+ @type file1: str
+ @param file2: File 2
+ @type file2: str
+ @param skipped_types: Tuple of strings specifying types of properties excluded from comparison.
+ Supported strings: type, mode, owner, group, device_number, xattr, atime, mtime, ctime, size, content
+ @type skipped_types: tuple of str
+ @rtype: tuple of str
+ @return: Tuple of strings specifying types of properties different between compared files
+ """
+
+ file1_stat = os.lstat(_unicode_encode(file1, encoding=_encodings["fs"], errors="strict"))
+ file2_stat = os.lstat(_unicode_encode(file2, encoding=_encodings["fs"], errors="strict"))
+
+ differences = []
+
+ if (file1_stat.st_dev, file1_stat.st_ino) == (file2_stat.st_dev, file2_stat.st_ino):
+ return ()
+
+ if "type" not in skipped_types and stat.S_IFMT(file1_stat.st_mode) != stat.S_IFMT(file2_stat.st_mode):
+ differences.append("type")
+ if "mode" not in skipped_types and stat.S_IMODE(file1_stat.st_mode) != stat.S_IMODE(file2_stat.st_mode):
+ differences.append("mode")
+ if "owner" not in skipped_types and file1_stat.st_uid != file2_stat.st_uid:
+ differences.append("owner")
+ if "group" not in skipped_types and file1_stat.st_gid != file2_stat.st_gid:
+ differences.append("group")
+ if "device_number" not in skipped_types and file1_stat.st_rdev != file2_stat.st_rdev:
+ differences.append("device_number")
+
+ if "xattr" not in skipped_types and sorted(xattr.get_all(file1, nofollow=True)) != sorted(xattr.get_all(file2, nofollow=True)):
+ differences.append("xattr")
+
+ if sys.hexversion >= 0x3030000:
+ if "atime" not in skipped_types and file1_stat.st_atime_ns != file2_stat.st_atime_ns:
+ differences.append("atime")
+ if "mtime" not in skipped_types and file1_stat.st_mtime_ns != file2_stat.st_mtime_ns:
+ differences.append("mtime")
+ if "ctime" not in skipped_types and file1_stat.st_ctime_ns != file2_stat.st_ctime_ns:
+ differences.append("ctime")
+ else:
+ if "atime" not in skipped_types and file1_stat.st_atime != file2_stat.st_atime:
+ differences.append("atime")
+ if "mtime" not in skipped_types and file1_stat.st_mtime != file2_stat.st_mtime:
+ differences.append("mtime")
+ if "ctime" not in skipped_types and file1_stat.st_ctime != file2_stat.st_ctime:
+ differences.append("ctime")
+
+ if "type" in differences:
+ pass
+ elif file1_stat.st_size != file2_stat.st_size:
+ if "size" not in skipped_types:
+ differences.append("size")
+ if "content" not in skipped_types:
+ differences.append("content")
+ else:
+ if "content" not in skipped_types:
+ if stat.S_ISLNK(file1_stat.st_mode):
+ file1_stream = io.BytesIO(os.readlink(_unicode_encode(file1,
+ encoding=_encodings["fs"],
+ errors="strict")))
+ else:
+ file1_stream = open(_unicode_encode(file1,
+ encoding=_encodings["fs"],
+ errors="strict"), "rb")
+ if stat.S_ISLNK(file2_stat.st_mode):
+ file2_stream = io.BytesIO(os.readlink(_unicode_encode(file2,
+ encoding=_encodings["fs"],
+ errors="strict")))
+ else:
+ file2_stream = open(_unicode_encode(file2,
+ encoding=_encodings["fs"],
+ errors="strict"), "rb")
+ while True:
+ file1_content = file1_stream.read(4096)
+ file2_content = file2_stream.read(4096)
+ if file1_content != file2_content:
+ differences.append("content")
+ break
+ if not file1_content or not file2_content:
+ break
+ file1_stream.close()
+ file2_stream.close()
+
+ return tuple(differences)