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

230 lines
8.4 KiB

# -*- coding: utf-8 -*-
# Copyright 2008-2016 Mir Calculate. 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 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 (filename not 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