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