# 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 # http://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])