[Sugar-devel] [PATCH 3/7] metadatastore: store/change files on disk defensively #2317
Martin Langhoff
martin at laptop.org
Thu Sep 20 23:55:36 EDT 2012
- only delete metadata files for keys that are being removed
- only write files when the data changes
- write/replace metadata files atomically, to avoid corrupting
existing data in case of an error
With this patch, we no longer corrupt metadata when trying
to edit/update a ds entry with the system hitting ENOSPC.
Signed-off-by: Martin Langhoff <martin at laptop.org>
---
src/carquinyol/metadatastore.py | 51 ++++++++++++++++++++++++++-------------
1 file changed, 34 insertions(+), 17 deletions(-)
diff --git a/src/carquinyol/metadatastore.py b/src/carquinyol/metadatastore.py
index 5967017..52cc10f 100644
--- a/src/carquinyol/metadatastore.py
+++ b/src/carquinyol/metadatastore.py
@@ -14,27 +14,46 @@ class MetadataStore(object):
if not os.path.exists(metadata_path):
os.makedirs(metadata_path)
else:
+ received_keys = metadata.keys()
for key in os.listdir(metadata_path):
- if key not in _INTERNAL_KEYS:
+ if key not in _INTERNAL_KEYS and key not in received_keys:
os.remove(os.path.join(metadata_path, key))
metadata['uid'] = uid
for key, value in metadata.items():
+ self._set_property(uid, key, value, md_path=metadata_path)
- # Hack to support activities that still pass properties named as
+ def _set_property(self, uid, key, value, md_path=False):
+ if not md_path:
+ md_path = layoutmanager.get_instance().get_metadata_path(uid)
+ # Hack to support activities that still pass properties named as
# for example title:text.
- if ':' in key:
- key = key.split(':', 1)[0]
-
- f = open(os.path.join(metadata_path, key), 'w')
- try:
- if isinstance(value, unicode):
- value = value.encode('utf-8')
- elif not isinstance(value, basestring):
- value = str(value)
- f.write(value)
- finally:
- f.close()
+ if ':' in key:
+ key = key.split(':', 1)[0]
+
+ changed = True
+ fpath = os.path.join(md_path, key)
+ tpath = os.path.join(md_path, '.' + key)
+ # FIXME: this codepath handles raw image data
+ # str() is 8-bit clean right now, but
+ # this won't last. We will need more explicit
+ # handling of strings, int/floats vs raw data
+ if isinstance(value, unicode):
+ value = value.encode('utf-8')
+ elif not isinstance(value, basestring):
+ value = str(value)
+
+ # avoid pointless writes; replace atomically
+ if os.path.exists(fpath):
+ stored_val = open(fpath, 'r').read()
+
+ if stored_val == value:
+ changed = False
+ if changed:
+ f = open(tpath, 'w')
+ f.write(value)
+ f.close()
+ os.rename(tpath, fpath)
def retrieve(self, uid, properties=None):
metadata_path = layoutmanager.get_instance().get_metadata_path(uid)
@@ -55,6 +74,4 @@ class MetadataStore(object):
return None
def set_property(self, uid, key, value):
- metadata_path = layoutmanager.get_instance().get_metadata_path(uid)
- property_path = os.path.join(metadata_path, key)
- open(property_path, 'w').write(value)
+ self._set_property(uid, key, value)
--
1.7.10.4
More information about the Sugar-devel
mailing list