blob: 30922a9018184bac4fb29470175071290eccc45f [file] [log] [blame]
# Copyright 2010-2012 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
__all__ = ['MtimeDB']
import copy
try:
import cPickle as pickle
except ImportError:
import pickle
import errno
import io
import json
import sys
import portage
from portage import _encodings
from portage import _unicode_decode
from portage import _unicode_encode
from portage.data import portage_gid, uid
from portage.localization import _
from portage.util import apply_secpass_permissions, atomic_ofstream, writemsg
class MtimeDB(dict):
# JSON read support has been available since portage-2.1.10.49.
_json_write = True
_json_write_opts = {
"ensure_ascii": False,
"indent": "\t",
"sort_keys": True
}
if sys.hexversion < 0x30200F0:
# indent only supports int number of spaces
_json_write_opts["indent"] = 4
def __init__(self, filename):
dict.__init__(self)
self.filename = filename
self._load(filename)
def _load(self, filename):
f = None
content = None
try:
f = open(_unicode_encode(filename), 'rb')
content = f.read()
except EnvironmentError as e:
if getattr(e, 'errno', None) in (errno.ENOENT, errno.EACCES):
pass
else:
writemsg(_("!!! Error loading '%s': %s\n") % \
(filename, e), noiselevel=-1)
finally:
if f is not None:
f.close()
d = None
if content:
try:
d = json.loads(_unicode_decode(content,
encoding=_encodings['repo.content'], errors='strict'))
except SystemExit:
raise
except Exception as e:
try:
mypickle = pickle.Unpickler(io.BytesIO(content))
try:
mypickle.find_global = None
except AttributeError:
# Python >=3
pass
d = mypickle.load()
except SystemExit:
raise
except Exception:
writemsg(_("!!! Error loading '%s': %s\n") % \
(filename, e), noiselevel=-1)
if d is None:
d = {}
if "old" in d:
d["updates"] = d["old"]
del d["old"]
if "cur" in d:
del d["cur"]
d.setdefault("starttime", 0)
d.setdefault("version", "")
for k in ("info", "ldpath", "updates"):
d.setdefault(k, {})
mtimedbkeys = set(("info", "ldpath", "resume", "resume_backup",
"starttime", "updates", "version"))
for k in list(d):
if k not in mtimedbkeys:
writemsg(_("Deleting invalid mtimedb key: %s\n") % str(k))
del d[k]
self.update(d)
self._clean_data = copy.deepcopy(d)
def commit(self):
if not self.filename:
return
d = {}
d.update(self)
# Only commit if the internal state has changed.
if d != self._clean_data:
d["version"] = str(portage.VERSION)
try:
f = atomic_ofstream(self.filename, mode='wb')
except EnvironmentError:
pass
else:
if self._json_write:
f.write(_unicode_encode(
json.dumps(d, **self._json_write_opts),
encoding=_encodings['repo.content'], errors='strict'))
else:
pickle.dump(d, f, protocol=2)
f.close()
apply_secpass_permissions(self.filename,
uid=uid, gid=portage_gid, mode=0o644)
self._clean_data = copy.deepcopy(d)