diff --git a/diffoscope/comparators/__init__.py b/diffoscope/comparators/__init__.py index 68f725c0..b15f67d1 100644 --- a/diffoscope/comparators/__init__.py +++ b/diffoscope/comparators/__init__.py @@ -36,6 +36,7 @@ class ComparatorManager: ("missing_file.MissingFile",), ("symlink.Symlink",), ("device.Device",), + ("socket_or_fifo.SocketOrFIFO",), ("debian.DotChangesFile", "debian_fallback.DotChangesFile"), ("debian.DotDscFile", "debian_fallback.DotDscFile"), ("debian.DotBuildinfoFile", "debian_fallback.DotBuildinfoFile"), diff --git a/diffoscope/comparators/binary.py b/diffoscope/comparators/binary.py index 1e1a20f9..46a9eeca 100644 --- a/diffoscope/comparators/binary.py +++ b/diffoscope/comparators/binary.py @@ -41,3 +41,7 @@ class FilesystemFile(File): def is_device(self): mode = os.lstat(self._name).st_mode return stat.S_ISCHR(mode) or stat.S_ISBLK(mode) + + def is_socketOrFIFO(self): + mode = os.lstat(self._name).st_mode + return stat.S_ISSOCK(mode) or stat.S_ISFIFO(mode) diff --git a/diffoscope/comparators/debian.py b/diffoscope/comparators/debian.py index 493da5fb..d77f59f9 100644 --- a/diffoscope/comparators/debian.py +++ b/diffoscope/comparators/debian.py @@ -62,6 +62,9 @@ class DebControlMember(File): def is_device(self): return False + def is_socketOrFIFO(self): + return False + class DebControlContainer(Container): def __init__(self, *args, **kwargs): diff --git a/diffoscope/comparators/decompile.py b/diffoscope/comparators/decompile.py index 5245c974..e0fc7b78 100644 --- a/diffoscope/comparators/decompile.py +++ b/diffoscope/comparators/decompile.py @@ -183,6 +183,9 @@ class AsmFunction(File): def is_device(self): return False + def is_socketOrFIFO(self): + return False + if tlsh: @property diff --git a/diffoscope/comparators/elf.py b/diffoscope/comparators/elf.py index 5f46a061..2e415737 100644 --- a/diffoscope/comparators/elf.py +++ b/diffoscope/comparators/elf.py @@ -329,6 +329,9 @@ class ElfSection(File): def is_device(self): return False + def is_socketOrFIFO(self): + return False + def has_same_content_as(self, other): # Always force diff of the section return False diff --git a/diffoscope/comparators/macho.py b/diffoscope/comparators/macho.py index ddfee2de..f72215c2 100644 --- a/diffoscope/comparators/macho.py +++ b/diffoscope/comparators/macho.py @@ -67,6 +67,9 @@ class MachoContainerFile(File, metaclass=abc.ABCMeta): def is_device(self): return False + def is_socketOrFIFO(self): + return False + def has_same_content_as(self, other): # Always force diff of the container return False diff --git a/diffoscope/comparators/missing_file.py b/diffoscope/comparators/missing_file.py index 2ef004ed..7a8285d9 100644 --- a/diffoscope/comparators/missing_file.py +++ b/diffoscope/comparators/missing_file.py @@ -73,6 +73,9 @@ class MissingFile(File, AbstractMissingType): def is_device(self): return False + def is_socketOrFIFO(self): + return False + def compare(self, other, source=None): # So now that comparators are all object-oriented, we don't have any # clue on how to perform a meaningful comparison right here. So we are diff --git a/diffoscope/comparators/squashfs.py b/diffoscope/comparators/squashfs.py index ebf13c2c..575a1bc0 100644 --- a/diffoscope/comparators/squashfs.py +++ b/diffoscope/comparators/squashfs.py @@ -33,6 +33,7 @@ from diffoscope.tempfiles import get_temporary_directory from .utils.file import File from .device import Device from .symlink import Symlink +from .socket_or_fifo import SocketOrFIFO from .directory import Directory from .utils.archive import Archive, ArchiveMember from .utils.command import Command @@ -72,6 +73,9 @@ class SquashfsMember(ArchiveMember): def is_device(self): return False + def is_socketOrFIFO(self): + return False + @property def path(self): # Use our extracted version and also avoid creating a temporary @@ -217,6 +221,40 @@ class SquashfsDevice(Device, SquashfsMember): return True +class SquashfsFIFO(SocketOrFIFO, SquashfsMember): + # Example line: + # crw-r--r-- root/root 0 2021-08-18 13:37 run/initctl + LINE_RE = re.compile( + r"^(?Ps|p)\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(?P.*)$" + ) + + KIND_MAP = {"s": stat.S_IFSOCK, "p": stat.S_IFIFO} + + @staticmethod + def parse(line): + m = SquashfsFIFO.LINE_RE.match(line) + if not m: + raise SquashfsInvalidLineFormat("invalid line format") + + d = m.groupdict() + try: + d["mode"] = SquashfsFIFO.KIND_MAP[d["kind"]] + del d["kind"] + except KeyError: + raise SquashfsInvalidLineFormat(f"unknown socket/FIFO kind {d['kind']}") + return d + + def __init__(self, archive, member_name, mode): + SquashfsMember.__init__(self, archive, member_name) + self._mode = mode + + def get_type(self): + return stat.S_IFMT(self._mode) + + def is_socketOrFIFO(self): + return True + + class SquashfsContainer(Archive): auto_diff_metadata = False @@ -225,6 +263,8 @@ class SquashfsContainer(Archive): "l": SquashfsSymlink, "c": SquashfsDevice, "b": SquashfsDevice, + "p": SquashfsFIFO, + "s": SquashfsFIFO, # Although it's unlikely that squashfs will support embedded sockets "-": SquashfsRegularFile, } diff --git a/diffoscope/comparators/utils/archive.py b/diffoscope/comparators/utils/archive.py index a5ac691c..89b41722 100644 --- a/diffoscope/comparators/utils/archive.py +++ b/diffoscope/comparators/utils/archive.py @@ -136,6 +136,9 @@ class ArchiveMember(File): def is_device(self): return False + def is_socketOrFIFO(self): + return False + class MissingArchiveLikeObject(AbstractMissingType): def getnames(self): diff --git a/diffoscope/comparators/utils/file.py b/diffoscope/comparators/utils/file.py index fd2a5847..367ce91b 100644 --- a/diffoscope/comparators/utils/file.py +++ b/diffoscope/comparators/utils/file.py @@ -327,6 +327,7 @@ class File(metaclass=abc.ABCMeta): (self.is_device, "device"), (self.is_symlink, "symlink"), (self.is_directory, "directory"), + (self.is_socketOrFIFO, "socket or FIFO"), ): if x(): return y @@ -374,6 +375,10 @@ class File(metaclass=abc.ABCMeta): def is_device(): raise NotImplementedError() + @abc.abstractmethod + def is_socketOrFIFO(): + raise NotImplementedError() + def compare_bytes(self, other, source=None): from .compare import compare_binary_files diff --git a/diffoscope/comparators/utils/libarchive.py b/diffoscope/comparators/utils/libarchive.py index fe815c1e..b54d38b2 100644 --- a/diffoscope/comparators/utils/libarchive.py +++ b/diffoscope/comparators/utils/libarchive.py @@ -24,6 +24,7 @@ import ctypes import logging import libarchive import collections +import stat from diffoscope.exc import ContainerExtractionError from diffoscope.config import Config @@ -34,6 +35,7 @@ from diffoscope.difference import Difference from ..device import Device from ..symlink import Symlink from ..directory import Directory +from ..socket_or_fifo import SocketOrFIFO from .archive import Archive, ArchiveMember @@ -187,6 +189,8 @@ class LibarchiveMember(ArchiveMember): def is_device(self): return False + def is_socketOrFIFO(self): + return False class LibarchiveDirectory(Directory, LibarchiveMember): def __init__(self, archive, entry): @@ -241,6 +245,18 @@ class LibarchiveDevice(Device, LibarchiveMember): return True +class LibarchiveFIFO(SocketOrFIFO, LibarchiveMember): + def __init__(self, container, entry): + LibarchiveMember.__init__(self, container, entry) + self._mode = entry.mode + + def get_type(self): + return stat.S_IFMT(self._mode) + + def is_socketOrFIFO(self): + return True + + class LibarchiveContainer(Archive): def open_archive(self): # libarchive is very very stream oriented an not for random access @@ -283,6 +299,8 @@ class LibarchiveContainer(Archive): return LibarchiveSymlink(self, entry) if entry.isblk or entry.ischr: return LibarchiveDevice(self, entry) + if entry.isfifo: + return LibarchiveFIFO(self, entry) return LibarchiveMember(self, entry)