fsimage.py 3.07 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# -*- coding: utf-8 -*-
#
# diffoscope: in-depth comparison of files, archives, and directories
#
# Copyright © 2015 Reiner Herrmann <reiner@reiner-h.de>
#
# diffoscope is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# diffoscope is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# 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 20

import re
21
import logging
22
import os.path
Chris Lamb's avatar
Chris Lamb committed
23 24

from diffoscope.difference import Difference
25

26
from .utils.file import File
27
from .utils.archive import Archive
Chris Lamb's avatar
Chris Lamb committed
28

29 30 31 32 33
try:
    import guestfs
except ImportError:
    guestfs = None

34 35
logger = logging.getLogger(__name__)

36 37

class FsImageContainer(Archive):
38
    def open_archive(self):
39 40 41
        if not guestfs:
            return None

42 43
        self.g = guestfs.GuestFS(python_return_dict=True)
        try:
44 45
            # force a check that LIBGUESTFS_CACHEDIR exists. otherwise guestfs
            # will fall back to /var/tmp, which we don't want
46 47 48 49
            self.g.set_cachedir(os.environ['LIBGUESTFS_CACHEDIR'])
        except KeyError:
            pass
        self.g.add_drive_opts(self.source.path, format='raw', readonly=1)
50 51 52
        try:
            self.g.launch()
        except RuntimeError:
53 54 55
            logger.exception("guestfs failed to launch")
            logger.error("If memory is too tight for 512 MiB, try running "
                         "with LIBGUESTFS_MEMSIZE=256 or lower.")
56
            return None
57
        devices = self.g.list_devices()
58
        self.g.mount(devices[0], '/')
59 60 61 62
        self.fs = self.g.list_filesystems()[devices[0]]
        return self

    def close_archive(self):
63 64
        if not guestfs:
            return None
65 66 67 68
        self.g.umount_all()
        self.g.close()

    def get_member_names(self):
69
        return [os.path.basename(self.source.path) + '.tar']
70 71 72 73

    def extract(self, member_name, dest_dir):
        dest_path = os.path.join(dest_dir, member_name)
        logger.debug('filesystem image extracting to %s', dest_path)
74
        self.g.tar_out('/', dest_path)
75 76 77

        return dest_path

78

79
class FsImageFile(File):
80
    DESCRIPTION = "ext2/ext3/ext4/btrfs filesystems"
81
    CONTAINER_CLASS = FsImageContainer
82
    FILE_TYPE_RE = re.compile(r'^(Linux.*filesystem data|BTRFS Filesystem).*')
83 84 85

    def compare_details(self, other, source=None):
        differences = []
86 87 88 89 90 91 92
        my_fs = ''
        other_fs = ''
        if hasattr(self.as_container, 'fs'):
            my_fs = self.as_container.fs
        if hasattr(other.as_container, 'fs'):
            other_fs = other.as_container.fs
        if my_fs != other_fs:
93 94 95
            differences.append(Difference.from_text(
                my_fs, other_fs, None, None, source="filesystem",
            ))
96

97
        return differences