aboutsummaryrefslogtreecommitdiff
blob: 587466589597457d095de80edd5ec085404ebb3d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# -*- coding: utf-8 -*-
# Copyright: 2009-2020 Gentoo Authors
# Author(s): Petteri Räty (betelgeuse@gentoo.org)
# License: GPL2

__all__ = ["database"]

import errno

from portage.cache import fs_template
from portage.versions import catsplit
from portage import cpv_getkey
from portage import os
from portage import _encodings
from portage import _unicode_decode
from portage.util._xattr import xattr


class NoValueException(Exception):
    pass


class database(fs_template.FsBased):

    autocommits = True

    def __init__(self, *args, **config):
        super(database, self).__init__(*args, **config)
        self.portdir = self.label
        self.ns = xattr.NS_USER + ".gentoo.cache"
        self.keys = set(self._known_keys)
        self.keys.add("_mtime_")
        self.keys.add("_eclasses_")
        # xattrs have an upper length
        self.max_len = self.__get_max()

    def __get_max(self):
        path = os.path.join(self.portdir, "profiles/repo_name")
        try:
            return int(self.__get(path, "value_max_len"))
        except NoValueException as e:
            maxattrlength = self.__calc_max(path)
            self.__set(path, "value_max_len", str(maxattrlength))
            return maxattrlength

    def __calc_max(self, path):
        """Find out max attribute length supported by the file system"""

        hundred = ""
        for i in range(100):
            hundred += "a"

        s = hundred

        # Could use finally but needs python 2.5 then
        try:
            while True:
                self.__set(path, "test_max", s)
                s += hundred
        except IOError as e:
            # ext based give wrong errno
            # https://bugzilla.kernel.org/show_bug.cgi?id=12793
            if e.errno in (errno.E2BIG, errno.ENOSPC):
                result = len(s) - 100
            else:
                raise

        try:
            self.__remove(path, "test_max")
        except IOError as e:
            if e.errno != errno.ENODATA:
                raise

        return result

    def __get_path(self, cpv):
        cat, pn = catsplit(cpv_getkey(cpv))
        return os.path.join(self.portdir, cat, pn, os.path.basename(cpv) + ".ebuild")

    def __has_cache(self, path):
        try:
            self.__get(path, "_mtime_")
        except NoValueException as e:
            return False

        return True

    def __get(self, path, key, default=None):
        try:
            return xattr.get(path, key, namespace=self.ns)
        except IOError as e:
            if not default is None and errno.ENODATA == e.errno:
                return default
            raise NoValueException()

    def __remove(self, path, key):
        xattr.remove(path, key, namespace=self.ns)

    def __set(self, path, key, value):
        xattr.set(path, key, value, namespace=self.ns)

    def _getitem(self, cpv):
        values = {}
        path = self.__get_path(cpv)
        attrs = {key: value for key, value in xattr.get_all(path, namespace=self.ns)}

        if not "_mtime_" in all:
            raise KeyError(cpv)

        # We default to '' like other caches
        for key in self.keys:
            attr_value = attrs.get(key, "1:")
            parts, sep, value = attr_value.partition(":")
            parts = int(parts)
            if parts > 1:
                for i in range(1, parts):
                    value += attrs.get(key + str(i))
            values[key] = value

        return values

    def _setitem(self, cpv, values):
        path = self.__get_path(cpv)
        max_len = self.max_len
        for key, value in values.items():
            # mtime comes in as long so need to convert to strings
            s = str(value)
            # We need to split long values
            value_len = len(s)
            parts = 0
            if value_len > max_len:
                # Find out how many parts we need
                parts = value_len / max_len
                if value_len % max_len > 0:
                    parts += 1

                # Only the first entry carries the number of parts
                self.__set(path, key, "%s:%s" % (parts, s[0:max_len]))

                # Write out the rest
                for i in range(1, parts):
                    start = i * max_len
                    val = s[start : start + max_len]
                    self.__set(path, key + str(i), val)
            else:
                self.__set(path, key, "%s:%s" % (1, s))

    def _delitem(self, cpv):
        pass  # Will be gone with the ebuild

    def __contains__(self, cpv):
        return os.path.exists(self.__get_path(cpv))

    def __iter__(self):

        for root, dirs, files in os.walk(self.portdir):
            for file in files:
                try:
                    file = _unicode_decode(
                        file, encoding=_encodings["fs"], errors="strict"
                    )
                except UnicodeDecodeError:
                    continue
                if file[-7:] == ".ebuild":
                    cat = os.path.basename(os.path.dirname(root))
                    pn_pv = file[:-7]
                    path = os.path.join(root, file)
                    if self.__has_cache(path):
                        yield "%s/%s/%s" % (cat, os.path.basename(root), file[:-7])