1# 2# diffoscope: in-depth comparison of files, archives, and directories 3# 4# Copyright © 2015 Jérémy Bobbio <lunar@debian.org> 5# Copyright © 2015-2020 Chris Lamb <lamby@debian.org> 6# 7# diffoscope is free software: you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation, either version 3 of the License, or 10# (at your option) any later version. 11# 12# diffoscope is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with diffoscope. If not, see <https://www.gnu.org/licenses/>. 19 20import os 21import stat 22import logging 23 24from diffoscope.tempfiles import get_named_temporary_file 25from diffoscope.difference import Difference 26 27from .binary import FilesystemFile 28from .utils.file import File 29 30logger = logging.getLogger(__name__) 31 32 33class Device(File): 34 DESCRIPTION = "character/block devices" 35 36 @classmethod 37 def recognizes(cls, file): 38 return file.is_device() 39 40 def get_device(self): 41 assert isinstance(self, FilesystemFile) 42 st = os.lstat(self.name) 43 return st.st_mode, os.major(st.st_rdev), os.minor(st.st_rdev) 44 45 def has_same_content_as(self, other): 46 try: 47 return self.get_device() == other.get_device() 48 except (AttributeError, OSError): 49 # 'other' is not a device, or something. 50 logger.debug("has_same_content: Not a device: %s", other) 51 return False 52 53 def create_placeholder(self): 54 with get_named_temporary_file(mode="w+", delete=False) as f: 55 f.write(format_device(*self.get_device())) 56 f.flush() 57 return f.name 58 59 @property 60 def path(self): 61 if not hasattr(self, "_placeholder"): 62 self._placeholder = self.create_placeholder() 63 return self._placeholder 64 65 def cleanup(self): 66 if hasattr(self, "_placeholder"): 67 os.remove(self._placeholder) 68 del self._placeholder 69 super().cleanup() 70 71 def compare(self, other, source=None): 72 with open(self.path) as my_content, open(other.path) as other_content: 73 return Difference.from_text_readers( 74 my_content, 75 other_content, 76 self.name, 77 other.name, 78 source=source, 79 comment="device", 80 ) 81 82 83def format_device(mode, major, minor): 84 if stat.S_ISCHR(mode): 85 kind = "character" 86 elif stat.S_ISBLK(mode): 87 kind = "block" 88 else: 89 kind = "weird" 90 return f"device:{kind}\nmajor: {major}\nminor: {minor}\n" 91