blob: 587466589597457d095de80edd5ec085404ebb3d [file] [log] [blame]
# -*- 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])