You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
calculate-utils-3-lib/pym/calculate/lib/utils/content.py

221 lines
7.9 KiB

#-*- 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"(?:(?P<sym>sym)\s+(?P<symname>.*)\s+->"
r"\s+(?P<symtarget>.*)\s+(?P<symtime>\S+)\s*)|"
r"(?:(?P<dirfif>dir|fif)\s+(?P<dirname>.*)\s*)|"
r"(?:(?P<obj>obj)\s+(?P<filename>.*)\s+"
r"(?P<md5>\S+)\s+(?P<filetime>\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