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.
221 lines
7.9 KiB
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
|