1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: AGPL-3.0
5 from __future__ import absolute_import
6 from builtins import bytes
13 from .fresh import FreshBase, convertTime
15 _logger = logging.getLogger('arvados.arvados_fuse')
17 class File(FreshBase):
18 """Base for file objects."""
20 __slots__ = ("inode", "parent_inode", "_mtime")
22 def __init__(self, parent_inode, _mtime=0):
23 super(File, self).__init__()
25 self.parent_inode = parent_inode
31 def readfrom(self, off, size, num_retries=0):
34 def writeto(self, off, size, num_retries=0):
35 raise Exception("Not writable")
50 class FuseArvadosFile(File):
51 """Wraps a ArvadosFile."""
53 __slots__ = ('arvfile',)
55 def __init__(self, parent_inode, arvfile, _mtime):
56 super(FuseArvadosFile, self).__init__(parent_inode, _mtime)
57 self.arvfile = arvfile
60 with llfuse.lock_released:
61 return self.arvfile.size()
63 def readfrom(self, off, size, num_retries=0):
64 with llfuse.lock_released:
65 return self.arvfile.readfrom(off, size, num_retries, exact=True)
67 def writeto(self, off, buf, num_retries=0):
68 with llfuse.lock_released:
69 return self.arvfile.writeto(off, buf, num_retries)
75 return self.arvfile.writable()
78 with llfuse.lock_released:
80 self.arvfile.parent.root_collection().save()
83 class StringFile(File):
84 """Wrap a simple string as a file"""
85 def __init__(self, parent_inode, contents, _mtime):
86 super(StringFile, self).__init__(parent_inode, _mtime)
87 self.contents = contents
90 return len(self.contents)
92 def readfrom(self, off, size, num_retries=0):
93 return bytes(self.contents[off:(off+size)], encoding='utf-8')
96 class ObjectFile(StringFile):
97 """Wrap a dict as a serialized json object."""
99 def __init__(self, parent_inode, obj):
100 super(ObjectFile, self).__init__(parent_inode, "", 0)
101 self.object_uuid = obj['uuid']
105 return self.object_uuid
107 def update(self, obj=None):
109 # TODO: retrieve the current record for self.object_uuid
110 # from the server. For now, at least don't crash when
111 # someone tells us it's a good time to update but doesn't
112 # pass us a fresh obj. See #8345
114 self._mtime = convertTime(obj['modified_at']) if 'modified_at' in obj else 0
115 self.contents = json.dumps(obj, indent=4, sort_keys=True) + "\n"
121 class FuncToJSONFile(StringFile):
122 """File content is the return value of a given function, encoded as JSON.
124 The function is called at the time the file is read. The result is
125 cached until invalidate() is called.
127 def __init__(self, parent_inode, func):
128 super(FuncToJSONFile, self).__init__(parent_inode, "", 0)
131 # invalidate_inode() is asynchronous with no callback to wait for. In
132 # order to guarantee userspace programs don't get stale data that was
133 # generated before the last invalidate(), we must disallow inode
135 self.allow_attr_cache = False
139 return super(FuncToJSONFile, self).size()
141 def readfrom(self, *args, **kwargs):
143 return super(FuncToJSONFile, self).readfrom(*args, **kwargs)
148 self._mtime = time.time()
150 self.contents = json.dumps(obj, indent=4, sort_keys=True) + "\n"