#-*- coding: utf-8 -*- # Copyright 2008-2013 Calculate Ltd. http://www.calculate-linux.org # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from calculate.lib.utils.files import (readLinesFile,readFile,pathJoin, process) from calculate.lib.utils.portage import reVerSplit from itertools import * from os import path import os import glob import re import hashlib class PkgContents: """ Object for work with CONTENTS file """ reCfg = re.compile(r"/\._cfg\d{4}_") reObj = re.compile( r"^(?:" r"(?:(?Psym)\s+(?P.*)\s+->" r"\s+(?P.*)\s+(?P\S+)\s*)|" r"(?:(?Pdir|fif)\s+(?P.*)\s*)|" r"(?:(?Pobj)\s+(?P.*)\s+" r"(?P\S+)\s+(?P\S+)\s*))$") def __init__(self,pkg,prefix="/"): self.prefix = prefix self.contentFile = path.join(prefix,'var/db/pkg/%s/CONTENTS'%pkg) self.readContents() def _identifyLine(self,line): res = self.reObj.search(line.strip()) if res: res = res.groupdict() if res.get('dirfif',''): return (res['dirname'],{'type':res['dirfif']}) elif res.get('obj',''): return (res['filename'],{'type':"obj", 'md5':res['md5'], 'mtime':res['filetime']}) elif res.get('sym',''): return (res['symname'],{'type':'sym', 'target':res['symtarget'], 'mtime':res['symtime']}) def readContents(self): """ Re-read contents """ self.content = dict(filter(None,map(self._identifyLine, readLinesFile(self.contentFile)))) def writeContents(self): """ Write current content """ f = open(self.contentFile,'w') for filename in sorted(self.content.keys(), key=lambda x:(1 if x.startswith('/etc') else 0,x)): value = self.content[filename] formats = {'obj':"obj {filename} {md5} {mtime}\n", 'sym':"sym {filename} -> {target} {mtime}\n", 'dir':"dir {filename}\n", 'fif':"fif {filename}\n"} f.write(formats[value['type']].format(filename=filename,**value)) f.close() def addDir(self,filename): filename = self.reCfg.sub("/",filename) if filename != '/': if not filename in self.content or \ self.content[filename]['type'] != 'dir': self.addDir(path.dirname(filename)) self.content[filename] = {'type':'dir'} def addFile(self,filename): newfilename = pathJoin(self.prefix,filename) filename = self.reCfg.sub("/",filename) self.content[filename] = {'type':'obj', 'md5':hashlib.md5(readFile(newfilename)).hexdigest(), 'mtime':str(int(os.stat(newfilename).st_mtime))} def addLink(self,filename): newfilename = pathJoin(self.prefix,filename) filename = self.reCfg.sub("/",filename) self.content[filename] = {'type':'sym', 'target':os.readlink(newfilename), 'mtime':str(int(os.lstat(newfilename).st_mtime))} def _fixNameByPrefix(self,filename): if self.prefix != '/' and filename.startswith(self.prefix): return filename[len(self.prefix):] return filename def removeObject(self,filename): filename = self._fixNameByPrefix(filename) filename = self.reCfg.sub("/",filename) if filename in self.content: self.content.pop(filename) def addObject(self,filename): """ Add object to content """ if filename != '/': filename = self._fixNameByPrefix(filename) newfilename = pathJoin(self.prefix,filename) self.addDir(path.dirname(filename)) if path.islink(newfilename): self.addLink(filename) elif path.isdir(newfilename): self.addDir(filename) elif path.isfile(newfilename): self.addFile(filename) def checkReserved(fileName,contentFile): """ Check contents with newContent """ TYPE,FILENAME,MD5,MTIME=0,1,2,3 obj = filter(lambda x:x[1] == fileName, map(lambda x:x.split(' '), filter(lambda x:x.startswith('obj'), readLinesFile(contentFile)))) # if pkg not content filename if not obj: return True # if file is not exists if not path.exists(fileName): return True contentMD5 = hashlib.md5(readFile(fileName)).hexdigest().strip() configMD5 = obj[0][MD5].strip() # if content was not changed if contentMD5 == configMD5: return True return False def checkContents(pkg,fileName,prefix='/',reservedFile=None): """ Check contents with newContent """ contentFile = path.join(prefix,'var/db/pkg/%s/CONTENTS'%pkg) if prefix != '/' and fileName.startswith(prefix): shortName = fileName[len(prefix):] else: shortName = fileName TYPE,FILENAME,MD5,MTIME=0,1,2,3 obj = filter(lambda x:x[1] == shortName, map(lambda x:x.split(' '), filter(lambda x:x.startswith('obj'), readLinesFile(contentFile)))) # if pkg not content filename if not obj: # for using reserved -CONTENTS file on postinst if not reservedFile or checkReserved(fileName,reservedFile): return True else: return False # if file is not exists if not path.exists(fileName): # for using reserved -CONTENTS file on postinst if not reservedFile or checkReserved(fileName,reservedFile): return True else: return False contentMD5 = hashlib.md5(readFile(fileName)).hexdigest().strip() configMD5 = obj[0][MD5].strip() # if content was not changed if contentMD5 == configMD5: return True return False def getCfgFiles(protected_dirs=['/etc'],prefix='/'): """ Get protected cfg files """ reCfg = re.compile(r"/\._cfg\d{4}_",re.S) findParams = ["find"]+map(lambda x:pathJoin(prefix,x),protected_dirs)+\ ["-name","._cfg????_*","!","-name",".*~","!","-iname",".*.bak", "-printf",r"%T@ %p\n"] mapCfg = {} for filetime,sep,filename in map(lambda x:x.partition(' '), filter(None,process(*findParams))): origFilename = reCfg.sub(r'/',filename) if not origFilename in mapCfg: mapCfg[origFilename] = [] mapCfg[origFilename].append((int(filetime.split('.')[0]),filename)) return mapCfg def fillContents(allContent,protected,prefix='/'): """ Fill dict file - package """ dbPath = pathJoin(prefix,'var/db/pkg') for contentFile in glob.glob(dbPath+"/*/*/CONTENTS"): for objFile in filter(lambda x:x.startswith('obj '), readLinesFile(contentFile)): res = PkgContents.reObj.search(objFile.strip()) if res: fn = res.groupdict()['filename'] if filter(lambda x:fn.startswith(x),protected): pkg = reVerSplit.search(os.path.dirname(contentFile)) if pkg: pkg = "%s/%s"%(pkg.groups()[:2]) allContent[fn] = pkg