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.
230 lines
8.4 KiB
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
|