aboutsummaryrefslogtreecommitdiff
blob: cc6b06246329455853ff027594db4d49074c24d2 (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
170
171
172
# -*- coding: utf-8 -*-
# Copyright: 2009-2011 Gentoo Foundation
# Author(s): Petteri Räty (betelgeuse@gentoo.org)
# License: GPL2

__all__ = ['database']

import errno

import portage
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
portage.proxy.lazyimport.lazyimport(globals(),
	'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:
			max = self.__calc_max(path)
			self.__set(path,'value_max_len',str(max))
			return max

	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
			else:
				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)
		all = {}
		for tuple in xattr.get_all(path,namespace=self.ns):
			key,value = tuple
			all[key] = value

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

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

		return values

	def _setitem(self, cpv, values):
		path = self.__get_path(cpv)
		max = 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:
				# Find out how many parts we need
				parts = value_len/max
				if value_len % max > 0:
					parts += 1

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

				# Write out the rest
				for i in range(1,parts):
					start = i * max
					val = s[start:start+max]
					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])