X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/682dd5b6cc23a455766a7651e3e841257660b31c..cd175e917038f68d80d29cc6fee24fc3b389069e:/services/fuse/arvados_fuse/fusefile.py diff --git a/services/fuse/arvados_fuse/fusefile.py b/services/fuse/arvados_fuse/fusefile.py index d3c13f3a16..81fcd405ad 100644 --- a/services/fuse/arvados_fuse/fusefile.py +++ b/services/fuse/arvados_fuse/fusefile.py @@ -1,6 +1,8 @@ +import json +import llfuse import logging import re -import json +import time from fresh import FreshBase, convertTime @@ -27,8 +29,8 @@ class File(FreshBase): def mtime(self): return self._mtime - def clear(self, force=False): - return True + def clear(self): + pass def writable(self): return False @@ -36,6 +38,7 @@ class File(FreshBase): def flush(self): pass + class FuseArvadosFile(File): """Wraps a ArvadosFile.""" @@ -44,13 +47,16 @@ class FuseArvadosFile(File): self.arvfile = arvfile def size(self): - return self.arvfile.size() + with llfuse.lock_released: + return self.arvfile.size() def readfrom(self, off, size, num_retries=0): - return self.arvfile.readfrom(off, size, num_retries, exact=True) + with llfuse.lock_released: + return self.arvfile.readfrom(off, size, num_retries, exact=True) def writeto(self, off, buf, num_retries=0): - return self.arvfile.writeto(off, buf, num_retries) + with llfuse.lock_released: + return self.arvfile.writeto(off, buf, num_retries) def stale(self): return False @@ -59,8 +65,9 @@ class FuseArvadosFile(File): return self.arvfile.writable() def flush(self): - if self.writable(): - self.arvfile.parent.root_collection().save() + with llfuse.lock_released: + if self.writable(): + self.arvfile.parent.root_collection().save() class StringFile(File): @@ -81,9 +88,55 @@ class ObjectFile(StringFile): def __init__(self, parent_inode, obj): super(ObjectFile, self).__init__(parent_inode, "", 0) - self.uuid = obj['uuid'] + self.object_uuid = obj['uuid'] self.update(obj) - def update(self, obj): + def uuid(self): + return self.object_uuid + + def update(self, obj=None): + if obj is None: + # TODO: retrieve the current record for self.object_uuid + # from the server. For now, at least don't crash when + # someone tells us it's a good time to update but doesn't + # pass us a fresh obj. See #8345 + return self._mtime = convertTime(obj['modified_at']) if 'modified_at' in obj else 0 self.contents = json.dumps(obj, indent=4, sort_keys=True) + "\n" + + def persisted(self): + return True + + +class FuncToJSONFile(StringFile): + """File content is the return value of a given function, encoded as JSON. + + The function is called at the time the file is read. The result is + cached until invalidate() is called. + """ + def __init__(self, parent_inode, func): + super(FuncToJSONFile, self).__init__(parent_inode, "", 0) + self.func = func + + # invalidate_inode() and invalidate_entry() are asynchronous + # with no callback to wait for. In order to guarantee + # userspace programs don't get stale data that was generated + # before the last invalidate(), we must disallow dirent + # caching entirely. + self.allow_dirent_cache = False + + def size(self): + self._update() + return super(FuncToJSONFile, self).size() + + def readfrom(self, *args, **kwargs): + self._update() + return super(FuncToJSONFile, self).readfrom(*args, **kwargs) + + def _update(self): + if not self.stale(): + return + self._mtime = time.time() + obj = self.func() + self.contents = json.dumps(obj, indent=4, sort_keys=True) + "\n" + self.fresh()