1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: AGPL-3.0
11 from .fresh import FreshBase, convertTime
13 _logger = logging.getLogger('arvados.arvados_fuse')
15 class File(FreshBase):
16 """Base for file objects."""
18 __slots__ = ("inode", "parent_inode", "_mtime")
20 def __init__(self, parent_inode, _mtime=0):
21 super(File, self).__init__()
23 self.parent_inode = parent_inode
29 def readfrom(self, off, size, num_retries=0):
32 def writeto(self, off, size, num_retries=0):
33 raise Exception("Not writable")
48 class FuseArvadosFile(File):
49 """Wraps a ArvadosFile."""
51 __slots__ = ('arvfile', '_enable_write')
53 def __init__(self, parent_inode, arvfile, _mtime, enable_write):
54 super(FuseArvadosFile, self).__init__(parent_inode, _mtime)
55 self.arvfile = arvfile
56 self._enable_write = enable_write
59 with llfuse.lock_released:
60 return self.arvfile.size()
62 def readfrom(self, off, size, num_retries=0):
63 with llfuse.lock_released:
64 return self.arvfile.readfrom(off, size, num_retries, exact=True, return_memoryview=True)
66 def writeto(self, off, buf, num_retries=0):
67 with llfuse.lock_released:
68 return self.arvfile.writeto(off, buf, num_retries)
74 return self._enable_write and self.arvfile.writable()
77 with llfuse.lock_released:
79 self.arvfile.parent.root_collection().save()
82 if self.parent_inode is None:
83 self.arvfile.fuse_entry = None
87 class StringFile(File):
88 """Wrap a simple string as a file"""
90 __slots__ = ("contents",)
92 def __init__(self, parent_inode, contents, _mtime):
93 super(StringFile, self).__init__(parent_inode, _mtime)
94 self.contents = contents
97 return len(self.contents)
99 def readfrom(self, off, size, num_retries=0):
100 return bytes(self.contents[off:(off+size)], encoding='utf-8')
103 class ObjectFile(StringFile):
104 """Wrap a dict as a serialized json object."""
106 __slots__ = ("object_uuid",)
108 def __init__(self, parent_inode, obj):
109 super(ObjectFile, self).__init__(parent_inode, "", 0)
110 self.object_uuid = obj['uuid']
114 return self.object_uuid
116 def update(self, obj=None):
118 # TODO: retrieve the current record for self.object_uuid
119 # from the server. For now, at least don't crash when
120 # someone tells us it's a good time to update but doesn't
121 # pass us a fresh obj. See #8345
123 self._mtime = convertTime(obj['modified_at']) if 'modified_at' in obj else 0
124 self.contents = json.dumps(obj, indent=4, sort_keys=True) + "\n"
130 class FuncToJSONFile(StringFile):
131 """File content is the return value of a given function, encoded as JSON.
133 The function is called at the time the file is read. The result is
134 cached until invalidate() is called.
137 __slots__ = ("func",)
139 def __init__(self, parent_inode, func):
140 super(FuncToJSONFile, self).__init__(parent_inode, "", 0)
143 # invalidate_inode() is asynchronous with no callback to wait for. In
144 # order to guarantee userspace programs don't get stale data that was
145 # generated before the last invalidate(), we must disallow inode
147 self.allow_attr_cache = False
151 return super(FuncToJSONFile, self).size()
153 def readfrom(self, *args, **kwargs):
155 return super(FuncToJSONFile, self).readfrom(*args, **kwargs)
160 self._mtime = time.time()
162 self.contents = json.dumps(obj, indent=4, sort_keys=True) + "\n"