diff options
author | 2019-08-07 19:06:11 +0200 | |
---|---|---|
committer | 2019-08-11 12:11:47 -0700 | |
commit | 0b7eda500a0dcb98a67f33bf9ef25b202b358986 (patch) | |
tree | 373d366bd8146432a2719261af3a5152fb3b0f2a /lib/portage/util/_compare_files.py | |
parent | lib/p/util/_dyn_libs/LinkageMapELF.py: get dep graph from EROOT. (diff) | |
download | portage-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.py | 103 |
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) |