# 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)